Files
Frametop--frametop/stream/spike/pattern.py
T
DeeJanuzandClaude Opus 5.5 2a675b9121 Remote displays: other computers' monitors as Frametop screens
A remote display is a screen of ft-screens whose picture comes from another computer,
streamed by Vibepollo with Moonlight's protocol. ft-screens (screens/remote.c) runs one
ft-stream per remote screen (101 and up) over a socket pair: ft-stream (stream/, GPLv3:
moonlight-common-c and moonlight-embedded's libgamestream, pinned) decodes on the iris
decoder into a ring of three RGBA buffers that ft-screens shows as a panel with a
screen's controls, layout, profiles, curve, pins and hand cutouts. Mouse, keys and scroll
go back to the host; a button's release goes through the stream it was pressed on, as
Vibepollo wants, and a press carried onto another remote panel moves the pointer there.
A panel out of sight gets 10 fps; the host's sound plays from one stream per host.

ft-stream signs in with an API token and the host's pinned public key
(stream/host.cpp), and chooses its path: a Steam Link dongle on the Frame's hotspot when
it answers, or the network (the layout's "route": auto, network or dongle). ft-layout
gets "remote" commands (list, add, connect, disconnect, host route and direct), starts
the connected displays with the desktop and keeps disconnected ones parked. ft-gaze and
ft-pointer know the remote panels. ft-screens now shuts its VR side down before its
streams and waits for SteamVR before exiting (a vrcompositor crash at quit, in standby).

docs/remote-displays.md has the design, the spikes and the test results.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 20:15:16 -06:00

55 lines
1.9 KiB
Python
Executable File

#!/usr/bin/env python3
"""Write the S1 colour test pattern as raw RGB24 (width x height x 3 bytes).
Bands from the top: 100% colour bars, 75% colour bars, a 16-step grey ramp,
near-black steps (0-30), near-white steps (225-255), and a black strip where the
clip's moving box runs. ft-dectest shows the same picture as an RGB reference next
to the decoded video, so a wrong YUV matrix or range shows as a visible difference:
range errors lift or crush the near-black and near-white steps, matrix errors shift
the bar hues.
usage: pattern.py WIDTH HEIGHT OUT.rgb
"""
import sys
BARS = [(1, 1, 1), (1, 1, 0), (0, 1, 1), (0, 1, 0), (1, 0, 1), (1, 0, 0), (0, 0, 1), (0, 0, 0)]
def row(width, colours):
"""One row of len(colours) equal columns; the last absorbs the remainder."""
out = bytearray()
n = len(colours)
for i, c in enumerate(colours):
x0, x1 = width * i // n, width * (i + 1) // n
out += bytes(c) * (x1 - x0)
return bytes(out)
def bands(height):
"""(rows, colours) per band, top to bottom; fractions of the height."""
split = [0.28, 0.20, 0.16, 0.14, 0.14]
rows = [int(height * f) for f in split]
rows.append(height - sum(rows)) # the box strip
level = lambda v: [tuple(int(round(v * 255 * k)) for k in c) for c in BARS]
return [
(rows[0], level(1.0)),
(rows[1], level(0.75)),
(rows[2], [(v, v, v) for v in (round(i * 255 / 15) for i in range(16))]),
(rows[3], [(v, v, v) for v in range(0, 32, 2)]),
(rows[4], [(v, v, v) for v in range(225, 256, 2)]),
(rows[5], [(0, 0, 0)]),
]
def main():
width, height, out = int(sys.argv[1]), int(sys.argv[2]), sys.argv[3]
with open(out, "wb") as f:
for n, colours in bands(height):
f.write(row(width, colours) * n)
# The box strip's top row, for make-clips.sh to place the moving box.
print(height - bands(height)[-1][0])
if __name__ == "__main__":
main()