Display controls (enum/get/set):
- Add PROTO_DISPLAY_CTRL_SCALE/ANCHOR/NO_SIGNAL_FPS constants to protocol.h
- handle_enum_controls: if device index maps to an active display slot,
return the three display controls (scale, anchor, no_signal_fps)
- handle_get_control: read display control values from slot under mutex
- handle_set_control: write display control values to slot under mutex;
scale/anchor are applied to the viewer by display_loop_tick each tick
Device IDs in enum-devices output:
- Proto_Display_Device_Info gains device_id field (wire format +2 bytes)
- handle_enum_devices computes device_id = total_v4l2 + display_index
- on_video_node/on_standalone callbacks take int* userdata to print [idx]
- on_display prints [device_id] from the wire field
Bug fix — protocol error on invalid device index:
- proto_read_enum_controls_response: early-return APP_OK after reading
status if status != OK; error responses have no count/data fields, so
the CUR_CHECK on count was failing with "payload too short"
Helpers added to main.c:
- count_v4l2_devices(): sum of media vnodes + standalone
- find_display_by_device_idx(): maps flat index to Display_Slot
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- controller_cli: drain semaphore and reset pending_cmd in do_connect
so stale posts from old connection don't unblock the next command
- protocol: add Proto_Display_Device_Info; extend
proto_write_enum_devices_response and proto_read_enum_devices_response
with display section; backward-compatible (absent in older messages)
- node: handle_enum_devices snapshots active Display_Slots under mutex
and includes them in the response
- controller_cli: on_display callback prints display window info in
enum-devices output
- query_cli: updated to pass NULL on_display (no display interest)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- readline replaces fgets — line editing and command history
- Discovery runs at startup (always); discovered peers print inline as they appear
- --host is now optional; without it, starts in discovery-only mode
- New REPL commands:
peers list discovered nodes with index
connect connect to first discovered peer
connect <idx> connect to peer by index
connect <host:port> connect directly
- connect switching closes the old connection before opening the new one
- Commands that require a connection print "not connected" when conn is NULL
- Makefile: add $(DISCOVERY_OBJ) and -lreadline to controller_cli link
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When a viewer window has no incoming stream, renders animated analog-TV
noise (hash-based, scanlines, phosphor tint) at configurable fps (default
15) with a centred "NO SIGNAL" text overlay.
- xorg: FRAG_NOSIGNAL_SRC shader + xorg_viewer_render_no_signal(v, time, noise_res)
- main: Display_Slot gains no_signal_fps + last_no_signal_t; display_loop_tick
drives no-signal render on idle slots via clock_gettime rate limiting
- protocol: START_DISPLAY extended by 2 bytes — no_signal_fps (0=default 15)
+ reserved; reader is backward-compatible (defaults 0 if length < 18)
- controller_cli: no_signal_fps optional arg on start-display
- docs: protocol.md updated with new field
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Protocol:
- Add PROTO_CMD_START_DISPLAY (0x000A) and PROTO_CMD_STOP_DISPLAY (0x000B)
with write/read functions; Proto_Start_Display carries stream_id, window
position/size, scale and anchor; PROTO_DISPLAY_SCALE_*/ANCHOR_* constants
Node display sink:
- Display_Slot struct with wanted_state/current_state (DISP_CLOSED/DISP_OPEN);
handlers set wanted state, display_loop_tick on main thread reconciles
- Up to MAX_DISPLAYS (4) simultaneous viewer windows
- on_frame routes incoming VIDEO_FRAME messages to matching display slot;
transport thread deposits payload, main thread consumes without holding lock
during JPEG decode/upload
- Main thread runs GL event loop when xorg is available; headless fallback
joins reconciler timer thread as before
Xorg multi-window:
- Ref-count glfwInit/glfwTerminate via glfw_acquire/glfw_release so closing
one viewer does not terminate GLFW for remaining windows
- Add glfwMakeContextCurrent before GL calls in push_yuv420, push_bgra,
push_mjpeg and poll so each viewer uses its own GL context correctly
Transport random port:
- Bind port 0 lets the OS assign a free port; getsockname reads it back
into server->bound_port after bind
- Add transport_server_get_port() accessor
- Default tcp_port changed from 8000 to 0 (random); node prints actual
port after server start so it is always visible in output
- Add --port PORT CLI override (before config-file argument)
controller_cli:
- Add start-display and stop-display commands
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Connects to a running video node by host:port. Supports:
enum-devices, enum-controls, get-control, set-control,
start-ingest, stop-ingest
Uses semaphore-based request/response synchronisation (same pattern as
query_cli). start-ingest maps directly to the new START_INGEST protocol
command with optional format/size/fps args; defaults to auto-select.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>