-
-
Notifications
You must be signed in to change notification settings - Fork 19
Description
Is this a new feature request?
- I have searched the existing issues
Wanted change
Summary
In current Wayland/Pixelflux paths using KDE X11 session (startplasma-x11 under Xwayland), clipboard domains can be split between:
- Wayland clipboard (used by Selkies
wl-copy/wl-paste) - X11 clipboard (used by Plasma X11 apps via X selections /
xclip)
This can cause clipboard sync to work in one direction/path but not consistently across both app domains without a manual bridge.
Environment
- Repo:
linuxserver/docker-baseimage-selkies - Distro path tested:
ubuntunoble - Wayland mode:
PIXELFLUX_WAYLAND=true - Session observed:
startplasma-x11,kwin_x11,Xwayland :1 - Wayland socket observed:
wayland-1 - Selkies clipboard monitor active
Problem
In this hybrid mode, users can observe:
- Browser/Selkies clipboard content reaches the Wayland domain
- X11 desktop apps do not always receive it automatically
- Reverse direction can also be inconsistent
This is understandable due to separate domains, but currently requires hand-rolled custom scripts.
Why this matters
- Wayland/Pixelflux support is newer and still evolving
- Clipboard UX is a core expectation for remote desktop
- Many “clipboard broken” reports are likely domain-bridge gaps in hybrid mode
Proposed solution
Add an optional custom service in baseimage:
- Service name example:
custom-svc-clipboard-bridge - Default: disabled (opt-in)
- Env toggle:
SELKIES_CLIPBOARD_BRIDGE_ENABLED=true|false(defaultfalse)
Bridge behavior
- Sync Wayland → X11 (
wl-paste->xclip) - Sync X11 → Wayland (
xclip -o->wl-copy) - Include loop suppression (hash/last-seen dedupe)
- Keep CPU/memory conservative (watch or low-frequency poll)
- Keep logs bounded/quiet
Why custom service
- Fits existing
s6service architecture cleanly - No heavy new dependencies (
wl-clipboardandxclipalready present) - Easy to disable
- Deterministic startup/runtime behavior
Acceptance criteria
-
With
SELKIES_CLIPBOARD_BRIDGE_ENABLED=false- behavior remains unchanged
-
With
SELKIES_CLIPBOARD_BRIDGE_ENABLED=true- Browser/Selkies clipboard becomes available in X11 desktop apps
- X11 app clipboard becomes visible to Wayland-side clipboard path (and outbound Selkies if enabled)
-
No feedback loop
- no sustained high CPU from bridge
- no unbounded memory growth caused by bridge
-
Works on hybrid path
- Wayland backend + Plasma X11/Xwayland session
Notes from local validation
- Hybrid process model confirmed (
startplasma-x11,kwin_x11,Xwayland :1) withwayland-1socket present - Clipboard domains are separate without explicit bridging
- Local custom service bridge resolves functional sync gap
Potential follow-up (optional)
- Direction controls:
SELKIES_CLIPBOARD_BRIDGE_DIRECTION=both|wayland-to-x11|x11-to-wayland
- Complementary to Selkies in/out clipboard controls
Reason for change
Clipboard currently does not work with pixelflux.
Proposed code change
`#!/usr/bin/with-contenv bash
set -eu
USER_NAME="abc"
BRIDGE_ENABLED="${SELKIES_CLIPBOARD_BRIDGE_ENABLED:-false}"
if [ "${BRIDGE_ENABLED}" != "true" ]; then
echo "[clipboard-bridge] disabled (SELKIES_CLIPBOARD_BRIDGE_ENABLED=${BRIDGE_ENABLED})" >&2
exec tail -f /dev/null
fi
DISPLAY="${DISPLAY:-:1}"
XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/config/.XDG}"
WAYLAND_DISPLAY="${WAYLAND_DISPLAY:-wayland-1}"
LOG_DIR="/config/.streamology/tmp"
LOG_FILE="${LOG_DIR}/clipboard-bridge.log"
mkdir -p "${LOG_DIR}"
chown "${USER_NAME}:${USER_NAME}" "${LOG_DIR}" || true
wait_for_endpoints() {
local tries=0
while [ ! -S "${XDG_RUNTIME_DIR}/${WAYLAND_DISPLAY}" ] || [ ! -S "/tmp/.X11-unix/X${DISPLAY#:}" ]; do
tries=$((tries + 1))
if [ $((tries % 20)) -eq 0 ]; then
echo "[clipboard-bridge] waiting for ${XDG_RUNTIME_DIR}/${WAYLAND_DISPLAY} and ${DISPLAY}" >&2
fi
sleep 1
done
}
clipboard_hash() {
printf '%s' "$1" | sha256sum | awk '{print $1}'
}
read_wayland_clipboard() {
s6-setuidgid "${USER_NAME}" env DISPLAY="${DISPLAY}" XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR}" WAYLAND_DISPLAY="${WAYLAND_DISPLAY}"
wl-paste --no-newline 2>/dev/null || true
}
read_x11_clipboard() {
s6-setuidgid "${USER_NAME}" env DISPLAY="${DISPLAY}" XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR}" WAYLAND_DISPLAY="${WAYLAND_DISPLAY}"
xclip -selection clipboard -o 2>/dev/null || true
}
write_wayland_clipboard() {
printf '%s' "$1" | s6-setuidgid "${USER_NAME}" env DISPLAY="${DISPLAY}" XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR}" WAYLAND_DISPLAY="${WAYLAND_DISPLAY}"
wl-copy >/dev/null 2>&1 || true
}
write_x11_clipboard() {
printf '%s' "$1" | s6-setuidgid "${USER_NAME}" env DISPLAY="${DISPLAY}" XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR}" WAYLAND_DISPLAY="${WAYLAND_DISPLAY}"
xclip -selection clipboard -in >/dev/null 2>&1 || true
}
echo "[clipboard-bridge] starting bidirectional bridge (Wayland <-> X11)" >&2
last_wayland_seen=""
last_x11_seen=""
last_from_wayland=""
last_from_x11=""
while true; do
wait_for_endpoints
wayland_text="$(read_wayland_clipboard)"
wayland_hash="$(clipboard_hash "${wayland_text}")"
if [ "${wayland_hash}" != "${last_wayland_seen}" ]; then
last_wayland_seen="${wayland_hash}"
if [ "${wayland_hash}" != "${last_from_x11}" ]; then
write_x11_clipboard "${wayland_text}"
last_from_wayland="${wayland_hash}"
last_x11_seen="${wayland_hash}"
echo "[clipboard-bridge] synced Wayland -> X11" >>"${LOG_FILE}" 2>&1 || true
fi
fi
x11_text="$(read_x11_clipboard)"
x11_hash="$(clipboard_hash "${x11_text}")"
if [ "${x11_hash}" != "${last_x11_seen}" ]; then
last_x11_seen="${x11_hash}"
if [ "${x11_hash}" != "${last_from_wayland}" ]; then
write_wayland_clipboard "${x11_text}"
last_from_x11="${x11_hash}"
last_wayland_seen="${x11_hash}"
echo "[clipboard-bridge] synced X11 -> Wayland" >>"${LOG_FILE}" 2>&1 || true
fi
fi
sleep 0.35
done`
Metadata
Metadata
Assignees
Labels
Type
Projects
Status