Feature/source plugins #1
No files matched your search
@@ -2893,6 +2893,16 @@ silence timeout and then resumes, which is the EMI case end to end.
|
|||||||
|
|
||||||
## Limitations
|
## Limitations
|
||||||
|
|
||||||
|
- **A remote display needs the software renderer, and the plot is empty until a signal is
|
||||||
|
enabled.** Over SSH with X11 forwarding there is no usable Vulkan device: GTK cannot create a
|
||||||
|
swapchain (`vkCreateSwapchainKHR … VK_ERROR_INITIALIZATION_FAILED`) and the window never appears
|
||||||
|
even though the stream connects and its CONFIG parses. `--renderer cairo` selects the software
|
||||||
|
preset for **both** halves — GTK's renderer and the plot's own GL context, which is a
|
||||||
|
`GtkGLArea` and asks for one regardless of what GSK draws with — and
|
||||||
|
`tools/udpscope-remote.sh` runs the app against a virtual X server with a VNC view carried over
|
||||||
|
an SSH tunnel, avoiding X11 forwarding altogether. If a window does appear but stays empty, check
|
||||||
|
that a **signal is enabled**: signals start disabled, so a session with none switched on plots
|
||||||
|
nothing and prints no `buffer allocated:` line, by design rather than as a fault.
|
||||||
- **Multi-element (vector/matrix) signals are acquired with a zero-order hold.**
|
- **Multi-element (vector/matrix) signals are acquired with a zero-order hold.**
|
||||||
`udps_client` decodes exactly one *sample* of `num_rows x num_cols` values per
|
`udps_client` decodes exactly one *sample* of `num_rows x num_cols` values per
|
||||||
DATA packet for such a signal, regardless of the publish mode, so it cannot be
|
DATA packet for such a signal, regardless of the publish mode, so it cannot be
|
||||||
|
|||||||
Executable
+135
@@ -0,0 +1,135 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
# SPDX-FileCopyrightText: 2026 Martino Ferrari
|
||||||
|
#
|
||||||
|
# Run udpscope on a headless machine and look at it over an SSH tunnel.
|
||||||
|
#
|
||||||
|
# Why this exists: over SSH with X11 forwarding there is no usable Vulkan device, so GTK cannot
|
||||||
|
# create a swapchain and the window never appears; `--renderer cairo` fixes GTK's half, but the
|
||||||
|
# plot is a GtkGLArea and indirect GLX is the second wall. A virtual X server on the machine that
|
||||||
|
# actually runs the app sidesteps both -- the GL context lives next door to the samples -- and VNC
|
||||||
|
# carries the picture back through a tunnel. `ssh -X` is not needed and not used.
|
||||||
|
#
|
||||||
|
# The picture is rendered by Xvfb's software path, so the app must be told to use its software
|
||||||
|
# preset too (`--renderer cairo`), otherwise GTK reaches for Vulkan inside Xvfb and warns exactly
|
||||||
|
# as it does over a forwarded display.
|
||||||
|
#
|
||||||
|
# x11vnc binds to localhost only and runs without a password *by design*: the SSH tunnel is the
|
||||||
|
# authentication. Never replace that with `-listen 0.0.0.0`, and never open the VNC port on the
|
||||||
|
# host's firewall. `-localhost` exists here so that a mistake is not a remotely reachable screen.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# tools/udpscope-remote.sh [--size WxH] [--port N] [--app PATH] [--no-vnc] [-- udpscope args...]
|
||||||
|
#
|
||||||
|
# --size WxH virtual screen (default 1600x1000)
|
||||||
|
# --port N VNC port on localhost (default 5900)
|
||||||
|
# --app PATH the binary to run (default build/udpscope)
|
||||||
|
# --no-vnc start the X server and the app only; for another viewer, or to test the server
|
||||||
|
# -- everything after this is passed to udpscope unchanged
|
||||||
|
#
|
||||||
|
# What it prints when it is up:
|
||||||
|
# * the ssh command to run on *your* machine, which forwards the VNC port,
|
||||||
|
# * the viewer command (any VNC client), and
|
||||||
|
# * optionally how to get a browser instead (websockify + noVNC, if installed).
|
||||||
|
#
|
||||||
|
# Exits when udpscope exits, or on Ctrl-C, taking the X server and the VNC server with it.
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
here=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd -P)
|
||||||
|
root=$(dirname -- "$here")
|
||||||
|
|
||||||
|
size=1600x1000
|
||||||
|
port=5900
|
||||||
|
app=${UDPSCOPE_REMOTE_APP:-$root/build/udpscope}
|
||||||
|
vnc=1
|
||||||
|
app_args=
|
||||||
|
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
|
case $1 in
|
||||||
|
--size) size=$2; shift 2 ;;
|
||||||
|
--port) port=$2; shift 2 ;;
|
||||||
|
--app) app=$2; shift 2 ;;
|
||||||
|
--no-vnc) vnc=0; shift ;;
|
||||||
|
--help|-h) sed -n '7,37p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
|
||||||
|
--) shift; app_args=$*; break ;;
|
||||||
|
*) echo "udpscope-remote: unknown option $1 (use -- to pass options to udpscope)" >&2; exit 2 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# Preflight, with the fix in the message rather than a bare "command not found".
|
||||||
|
need() {
|
||||||
|
command -v "$1" >/dev/null 2>&1 || {
|
||||||
|
echo "udpscope-remote: $1 is required. $2" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
need Xvfb "Install Xvfb (Arch: pacman -S xorg-server-xvfb; Debian: apt install xvfb)."
|
||||||
|
if [ "$vnc" = 1 ]; then
|
||||||
|
need x11vnc "Install x11vnc (Arch: pacman -S x11vnc; Debian: apt install x11vnc), or pass --no-vnc."
|
||||||
|
fi
|
||||||
|
[ -x "$app" ] || { echo "udpscope-remote: $app is not executable; build it first or pass --app" >&2; exit 1; }
|
||||||
|
|
||||||
|
# A free display: probe the ones a desktop is unlikely to be using and take the first that answers
|
||||||
|
# no. `xdpyinfo` failing is the test -- a stale lock file does not matter, only a live server does.
|
||||||
|
display=
|
||||||
|
n=90
|
||||||
|
while [ "$n" -le 120 ]; do
|
||||||
|
if ! xdpyinfo -display ":$n" >/dev/null 2>&1; then display=":$n"; break; fi
|
||||||
|
n=$((n + 1))
|
||||||
|
done
|
||||||
|
[ -n "$display" ] || { echo "udpscope-remote: no free display in :90..:120" >&2; exit 1; }
|
||||||
|
|
||||||
|
xvfb_pid=
|
||||||
|
app_pid=
|
||||||
|
vnc_pid=
|
||||||
|
cleanup() {
|
||||||
|
[ -n "${vnc_pid:-}" ] && kill "$vnc_pid" 2>/dev/null || true
|
||||||
|
[ -n "${app_pid:-}" ] && kill "$app_pid" 2>/dev/null || true
|
||||||
|
[ -n "${xvfb_pid:-}" ] && kill "$xvfb_pid" 2>/dev/null || true
|
||||||
|
wait 2>/dev/null || true
|
||||||
|
}
|
||||||
|
trap cleanup EXIT INT TERM
|
||||||
|
|
||||||
|
# `-nolisten tcp` too: the virtual server is for this machine's sockets only.
|
||||||
|
Xvfb "$display" -screen 0 "${size}x24" -nolisten tcp >/dev/null 2>&1 &
|
||||||
|
xvfb_pid=$!
|
||||||
|
for _ in 1 2 3 4 5 6 7 8 9 10; do
|
||||||
|
xdpyinfo -display "$display" >/dev/null 2>&1 && break
|
||||||
|
sleep 0.2
|
||||||
|
done
|
||||||
|
xdpyinfo -display "$display" >/dev/null 2>&1 || { echo "udpscope-remote: Xvfb did not come up on $display" >&2; exit 1; }
|
||||||
|
|
||||||
|
export DISPLAY="$display"
|
||||||
|
# The software preset, as one option: GTK's renderer *and* the GL-area path (see --renderer's help).
|
||||||
|
[ "$vnc" = 1 ] && echo "udpscope-remote: screen $display ${size}x24, vnc on localhost:$port"
|
||||||
|
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
"$app" --renderer cairo $app_args &
|
||||||
|
app_pid=$!
|
||||||
|
|
||||||
|
if [ "$vnc" = 1 ]; then
|
||||||
|
x11vnc -display "$display" -localhost -nopw -forever -shared -quiet -rfbport "$port" &
|
||||||
|
vnc_pid=$!
|
||||||
|
sleep 1
|
||||||
|
cat <<EOF
|
||||||
|
|
||||||
|
View it from your machine (the tunnel carries the VNC port; nothing else is exposed):
|
||||||
|
|
||||||
|
ssh -N -L $port:localhost:$port $(id -un)@<this-host>
|
||||||
|
|
||||||
|
then, in another terminal on your machine:
|
||||||
|
|
||||||
|
vncviewer localhost:$port # or any VNC client
|
||||||
|
|
||||||
|
A browser instead of a VNC client, if websockify and noVNC are installed here:
|
||||||
|
|
||||||
|
websockify 6080 localhost:$port &
|
||||||
|
# ...and open http://localhost:6080/vnc.html through a second tunnel:
|
||||||
|
# ssh -N -L 6080:localhost:6080 $(id -un)@<this-host>
|
||||||
|
|
||||||
|
Ctrl-C here, or quitting udpscope, closes the app and both servers.
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
# The app owns the run: when it exits, so does everything else (the trap).
|
||||||
|
wait "$app_pid" || true
|
||||||
Reference in new issue
Block a user