Add Frametop Remote Access, a settings app for the VNC view

A GTK 4 / libadwaita app (remote/ft-remote-settings, on the host's own
Python like the gaze probe): remote access on and off (REMOTE, applied at
once when the desktop allows it), the tailnet name and address to connect
to, and the VNC password shown, copied, or replaced. The password stays
random and made on the Frame, in ~/.config/frametop-remote; none is in
the code.

session/remote-ctl.sh starts, stops, and reports remote access; the
session uses it and leaves a remote-capable marker, since KWin allows the
capture only in a desktop that started with REMOTE=1. The password
between krdp and the VNC bridge (local only, but on krdp's command line)
is now new at every start. The installer adds the app to the menu.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
DeeJanuzandClaude Opus 5.5 committed 2026-09-30 15:55:49 -06:00
1 parent 9820e7996f
commit 1f19732aaf
9 files changed
+339 -10

No files matched your search

+5 -5
View File
@@ -114,9 +114,7 @@ host_runtime=$XDG_RUNTIME_DIR
runtime=$host_runtime/frametop
cleanup() {
pkill -f '[k]rdpserver --plasma' 2>/dev/null || true
pkill -f '[X]vnc :20 ' 2>/dev/null || true
pkill -f '[x]freerdp /v:.*:3390' 2>/dev/null || true
"$here/remote-ctl.sh" stop
fusermount3 -u -z "$runtime/doc" 2>/dev/null || true
umount --recursive "$runtime" 2>/dev/null || true
rm -rf "$runtime"
@@ -173,10 +171,12 @@ mkdir -p "$XDG_CONFIG_HOME" "$XDG_STATE_HOME"
# match it to an installed app. KWin's permission check for screencast and fake
# input is turned off for this nested session only, and so is the check on KWin's
# D-Bus screenshot interface, which scripts use to see the screens without the headset.
# The marker lets remote-ctl.sh (and Frametop Remote Access) start and stop it later in
# this session; a desktop started without it can't capture.
if [ "$remote" = 1 ]; then
export KWIN_WAYLAND_NO_PERMISSION_CHECKS=1 KWIN_SCREENSHOT_NO_PERMISSION_CHECKS=1
"$here/remote-desktop.sh" "$runtime" > /tmp/frametop-remote.log 2>&1 &
"$here/vnc-bridge.sh" > /tmp/frametop-vnc.log 2>&1 &
touch "$runtime/remote-capable"
"$here/remote-ctl.sh" start
fi
# ft-floatd (floating windows) runs inside the Plasma session, on its D-Bus: started from
+1 -1
View File
@@ -6,7 +6,7 @@ SCREENS=2 # gamescope: number of desktop screens (VR panels)
WIDTH=1920 # gamescope: pixels per screen (at most 1920x1080 worth)
HEIGHT=1080
PHYS_WIDTH=1.6 # gamescope: panel width in metres, docked
REMOTE=0 # 1 = serve the desktop over VNC on the tailnet (port 5900; see README)
REMOTE=0 # 1 = serve the desktop's primary screen over VNC on the tailnet (port 5900; Frametop Remote Access, see README)
FLOAT_SLOTS=8 # screens backend: how many windows can float in VR at once (spare outputs; 0 turns it off; restart the desktop after a change)
FLOAT_MARGIN=300 # floating windows: pixels around each window on its output, so menus have room past its edges
POINTER=0 # 1 = the mouse drives the universal 3D pointer (ft_pointer driver) instead of a plain mouse
+58
View File
@@ -0,0 +1,58 @@
#!/bin/bash
# Start, stop, or ask about remote access to the Frametop desktop (VNC over the tailnet):
# remote-desktop.sh (krdp capturing the desktop on 127.0.0.1) and vnc-bridge.sh (VNC of its
# primary screen). Runs on the Frame host; frametop-session.sh and Frametop Remote Access
# (remote/ft-remote-settings) use it.
#
# remote-ctl.sh start | stop | restart | status
#
# Capture only works in a desktop that started with REMOTE=1: KWin's permission checks for
# screencast and fake input are turned off when it starts, and only then. Such a session
# leaves $runtime/remote-capable; without it, start says so (exit 3) and the setting applies
# at the next desktop start. status prints key=value lines: capable, running, address, name,
# port.
set -u
here=$(dirname "$(readlink -f "$0")")
runtime=/run/user/$(id -u)/frametop
vnc_port=${VNC_PORT:-5900}
running() { pgrep -f '[X]vnc :20 ' >/dev/null && pgrep -f '[k]rdpserver --plasma' >/dev/null; }
stop() {
pkill -f "[v]nc-bridge.sh" 2>/dev/null
pkill -f "[r]emote-desktop.sh" 2>/dev/null
pkill -f '[k]rdpserver --plasma' 2>/dev/null
pkill -f '[X]vnc :20 ' 2>/dev/null
pkill -f '[x]freerdp /v:127.0.0.1:' 2>/dev/null
return 0
}
start() {
if [ ! -e "$runtime/remote-capable" ]; then
echo "this desktop didn't start with remote access on: it applies at the next desktop start" >&2
return 3
fi
running && return 0
stop
setsid "$here/remote-desktop.sh" "$runtime" > /tmp/frametop-remote.log 2>&1 < /dev/null &
setsid "$here/vnc-bridge.sh" > /tmp/frametop-vnc.log 2>&1 < /dev/null &
return 0
}
case ${1:-status} in
start) start ;;
stop) stop ;;
restart) stop; sleep 1; start ;;
status)
addr=$(ip -4 -o addr show tailscale0 2>/dev/null | awk '{print $4}' | cut -d/ -f1)
name=$(curl -s --max-time 2 --unix-socket /run/tailscale/tailscaled.sock \
http://local-tailscaled.sock/localapi/v0/status 2>/dev/null |
python3 -c 'import json, sys; print(json.load(sys.stdin)["Self"]["DNSName"].rstrip("."))' 2>/dev/null)
echo "capable=$([ -e "$runtime/remote-capable" ] && echo 1 || echo 0)"
echo "running=$(running && echo 1 || echo 0)"
echo "address=$addr"
echo "name=$name"
echo "port=$vnc_port" ;;
*) echo "usage: $0 start|stop|restart|status" >&2; exit 2 ;;
esac
+3 -3
View File
@@ -12,9 +12,9 @@ port=${RDP_PORT:-3390}
creds=$HOME/.config/frametop-remote
mkdir -p -m 0700 "$creds"
if [ ! -s "$creds/password" ]; then
(umask 077; head -c 24 /dev/urandom | base64 | tr -d '/+=' | cut -c1-20 > "$creds/password")
fi
# The password between krdp and vnc-bridge.sh (both on this host), new at every start: krdp
# takes it only on its command line, which other local users can read while it runs.
(umask 077; head -c 24 /dev/urandom | base64 | tr -d '/+=' | cut -c1-20 > "$creds/password")
if [ ! -s "$creds/cert.pem" ]; then
(umask 077; openssl req -x509 -newkey rsa:2048 -nodes -days 3650 -subj /CN=steam-frame \
-keyout "$creds/key.pem" -out "$creds/cert.pem" 2>/dev/null)