[documentation] Howto clipboard
Context
The roadmap in the main readme still lists clipboard unchecked:
- [ ] Clipboard support
While technically true, as lan-mouse's own protocol doesn't (and can't easily for technical reasons around UDP realtime communications) carry clipboard content, in practice clipboard sharing works like a charm (see #105) thanks to hooks, and external tools such as wl-clipboard/wl-clipboard-rs and xclip (for clipboard management) and SSH forced commands (for encryption and security) or Syncthing (keeping clipboard history synced)
In this HOWTO issue, several users have documented their respective solutions for sharing clipboard content.
TODO
Link this issue from the roadmap.
UPDATE
- I have written a documentation about my script
- @kevinJ045 has documented his solution
- @tmcnulty387's solution is also documented
- @paperluigis mentions KDE connect
- I am trying to encourage other users to document their hacks/solutions
Turns out the resource you need is either so scarce or no longer exists. But it's alright, if i manage to get a way to get clipboard or drag and drop work i'll comment it here in detail until you're back.
Here's what i am doing to have it sync my clipboard:
#!/bin/bash
ips=($(cat ~/.config/clip-iplist))
clsport=${1:-52111}
listen_clipboard() {
for ip in "${ips[@]}"; do
socat TCP-LISTEN:$clsport,fork EXEC:"wl-copy" &
done
}
send_clipboard() {
while clipnotify; do
content=$(wl-paste -n)
for ip in "${ips[@]}"; do
echo "$content" | socat - TCP:$ip:$clsport
done
done
}
listen_clipboard &
send_clipboard
Just keep the IPs you wanna sync in ~/.config/clip-iplist and then run this. You might wanna install socat and clipnotify if you don't have them.
Issues:
- I tried
clipnotifyonswayand it didn't seem to work... But it's open source so if you need to you can clone the github repo and editclipnotify.c.
It's not 100% solid, but i am using it right now.
And here is my script which is SSH-based instead of netcat/socat/bash's /dev/tcp/…/etc. clear-text network pipes (It should be a bit safer for copy-pasting passwords around).
Key points
- uses
sshfor encrypted transmission, instead of clear-text network pipes. - thus it also uses TCP instead of lan-mouse's UDP for content integrity.
- is single peer-2-peer exchange instead of broadcast/distributed so it's suitable for using in lan-mouse config.toml's option
enter_hook. - it relies on SSH keys and forced commands and works in the push direction to limit attack surface.
- can work on Wayland and X11 senders and receivers.
Limitations
- Only works on Linux with Wayland and X11
- Does NOT work on Mac OS nor Windows as those environment are completely alien to me (somebody jump in?)
- lan-mouse cannot (yet) detect mouse leaving on X11 clients, so will not call the
entry_hook, but you can still use the script from command line.
Usage
$ pushclipboard -h
Usage:
/home/deck/projects/shell-utils/pushclipboard [ -v ] [ -t {n} ] [ -4 | -6 ] [ -x | -w ] [ -i sshkey ] {target computer}
Copies the content of Wayland's clipboard to the target computer
Options:
-t {n} timeout for n seconds
-4 connect SSH over IPv4
-6 connect SSH over IPv6
-w use Wayland (auto-detected)
-x use X11 (fall-back)
-v verbose
-i [sshkey] Use sshkey as identity
-f build authorized_keys' forced-command line
- if you don't manually specify a key with
-ithe script tries to search one namedid_… blah …_clipboard- failing that is simply calls whatever you have in your ssh configuration
- the script tries to auto-detect whether Wayland or X11 runs locally based on standard environment variables
- you can override with
-wor-x
- you can override with
- if you haven't setup a forced command, the script will try remotely running the
wl-copycommand at the SSH receiver.
[!CAUTION] You absolutely SHOULD consider setting up SSH forced commands as per the Howto bellow for security reasons, even if
pushclipboardmay work without.
Howto
This is how you configure your local (sender) machine, so that it can push the content of the clipboard to a remote (receiver) machine. This can work no matter what display server the desktop environment uses at each end-point, you can mix and match Wayland or X11.
SSH Key
First you need to create a passphrase-less SSH key-pair dedicated to this script:
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_clipboard
output of `ssh-keygen`
Generating public/private ed25519 key pair.
Enter passphrase for "/home/dryak/.ssh/id_ed25519_clipboard" (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/dryak/.ssh/id_ed25519_clipboard
Your public key has been saved in /home/dryak/.ssh/id_ed25519_clipboard.pub
The key fingerprint is:
SHA256:HRsePTditXVegjurN7o+Rq3R5gTThnXwkM/nX8yJV1M deck@steamdeck
The key's randomart image is:
+--[ED25519 256]--+
| o+o +|
| .++.=E|
| ++=== o|
| o+*=+ooo|
| S +* o.=+|
| o *. o=|
| . B . o|
| = + .|
| o+= . |
+----[SHA256]-----+
[!TIP]
- just press enter with no passphrase (we will reduce the attack surface with forced commands)
- you can replace
ed25519with your favorite algo- use a filename that looks like
id_… blah …_clipboardso that the script can automatically find it.
SSH Forced Commands
generate
We will use forced commands, so that in case of leaked key, the worst that can happen is random shit copied into the clipboard (not even the clipboard content could be inspected, and no arbitrary commands could be run).
Use the -f option of the script to show the lines that should be added to the remote machine:
pushclipboard -f
output of `pushclipboard -f`
Found key: /home/dryak/.ssh/id_ed25519_clipboard
# forced command in ~/.ssh/authorized_keys
# Wayland
command="/usr/bin/env WAYLAND_DISPLAY='wayland-0' wl-copy; echo copied",no-agent-forwarding,no-pty,no-X11-forwarding ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK0a9JD/Hwda0HdFKTk64nLAYoPDUFezEHgOjpCqvMwC dryak@nereid
# X11
command="xclip -display :0 -in -rmlastnl -selection clipboard; echo copied",no-agent-forwarding,no-pty,no-X11-forwarding ssh-ed25519 AAAAAC3NzaC1lZDI1NTE5AAAAIK0a9JD/Hwda0HdFKTk64nLAYoPDUFezEHgOjpCqvMwC dryak@nereid
configure remote receiving end
Depending on what desktop environment the remote (receiver) machine is running, copy the appropriate command="…etc line for either Wayland or X11 display server, and add it to the .ssh/authorized_keys file on that remote (receiver) machine:
[deck@steamdeck ~]$ nano ~/.ssh/authorized_keys
[!TIP] if the .ssh configuration directory does not exist for that user on the remote machine, you need to create it with the approriate access rights:
mkdir -m 0700 ~/.ssh
Test it on the CLI
On the local (sender) machine, copy something into the clipboard, either with the mouse, the keyboard, or the command line:
date --rfc-3339=seconds | wl-copy
Try pushing the content of the keyboard to the remote (receiver) machine using the script on the command line interface:
pushclipboard steamdeck.local
output of `pushclipboard steamdeck.local`
Found key: /home/dryak/.ssh/id_ed25519_clipboard
Pushing clipboard to steamdeck.local using wl-paste on Wayland...
copied
[!TIP]
- the script will attempt automatically detecting whether the local (sender) machine runs Wayland or X11 and will use
wl-pasteorxclipaccordingly.- if you used a filename that looks like
id_… blah …_clipboardthe script can automatically find the SSH key.
Check that the remote (receiver) machine successfully put the string in its clipboard, either by checking the clipboard, or by pasting with the mouse, the keyboard, or the command line:
[deck@steamdeck ~]$ xclip -o -selection clipboard
2025-07-25 12:03:23+02:00
or
[dryak@nereid ~]$ wl-paste
2025-07-25 12:03:23+02:00
Configure lan-mouse
On the local (sender) machine you can edit the configuration file:
nano ~/.config/lan-mouse/config.toml
and add the enter_hook option for the clients entry specific to that remote (receive) machine:
# …other clients…
[[clients]]
position = "right"
hostname = "steamdeck.home"
activate_on_startup = true
ips = ["10.0.1.16"]
port = 4242
enter_hook="/home/dryak/bin/pushclipboard steamdeck.local"
[!IMPORTANT]
- Due to lan-mouse not yet supporting X11 capture, you can only configure Wayland lan-mouse instance with this hook (X11 instances will never detect the mouse attempting to leaving screen and never trigger entering into another instance).
- You might need to give the full path to this script depending on how it is installed. E.g., some distro (such as the Arch Linux demonstrated above) do not include
$HOME/binin their PATH.
Test it in lan-mouse
Now test that in lan-mouse, when moving the cursor from a Wayland Linux instance to another Linux instance, the clipboard gets copied over.
lan-mouse's logs
…
[2025-07-25T10:46:28Z WARN lan_mouse::capture] releasing capture: not connected
[2025-07-25T10:46:28Z INFO lan_mouse::connect] client 1 connecting ...
[2025-07-25T10:46:28Z INFO lan_mouse::connect] client (1) connecting ... (ips: [10.0.1.16:4242])
[2025-07-25T10:46:28Z INFO lan_mouse::connect] connecting to 10.0.1.16:4242 ...
[2025-07-25T10:46:28Z INFO lan_mouse::service] entering client 1 ...
[2025-07-25T10:46:28Z INFO lan_mouse::service] spawning command!
Found key: /home/dryak/.ssh/id_ed25519_clipboard
Pushing clipboard to steamdeck.local using wl-paste on Wayland...
copied
[2025-07-25T10:46:29Z INFO lan_mouse::service] /home/dryak/bin/pushclipboard steamdeck.local exited successfully
…
Rinse and repeat
You can repeat the above steps to add more machines.
[!TIP] Depending on how paranoid you are with your keys, you can skip the SSH Keys generation, and simply reuse the
.ssh/id_ed25519_clipboardand~/.ssh/config/id_ed25519_clipboard .pubfiles on all your machine (and copy the same line into~/.ssh/authorized_keysAt worst, if a key is leaked, an attacker could at best only fill your clipboard with crap, not even inspect the clipboard content, nor run arbitrary commands.
[!IMPORTANT] Due to lan-mouse not yet supporting X11 capture, you can only configure Wayland lan-mouse instance with the hook. On machine running X11, you can still run
pushclipboard …target…from the command line, though.
Source code
pushclipboard
#!/bin/bash
declare -a timeout
declare -a read_t
declare -a ssh_options
ssh_key=
buildline=0
gfxsys=
usage() {
cat <<HELP
Usage:
$0 [ -v ] [ -t {n} ] [ -4 | -6 ] [ -x | -w ] [ -i sshkey ] {target computer}
Copies the content of Wayland's clipboard to the target computer
Options:
-t {n} timeout for n seconds
-4 connect SSH over IPv4
-6 connect SSH over IPv6
-w use Wayland (auto-detected)
-x use X11 (fall-back)
-v verbose
-i [sshkey] Use sshkey as identity
-f build authorized_keys' forced-command line
HELP
exit "$1"
}
# Process options
while getopts "t:46wxvi:f-:h" o; do
case "${o}" in
t)
timeout=( timeout "${OPTARG}" )
read_t=( '-t' "${OPTARG}" )
;;
4|6|v) ssh_options+=( "-${o}" ) ;;
i) ssh_key="${OPTARG}"
if [[ ! -r "${ssh_key}" ]]; then
echo "Cannot read ${ssh_key}" 1>&2
exit 2
else
echo "Using ${ssh_key}"
fi
;;
f) buildline=1 ;;
w) gfxsys=wayland ;;
x) gfxsys=x11 ;;
h) usage 0 ;;
-) case "${OPTARG}" in
help) usage 0 ;;
*) printf 'Bad option %s\n\n' "${OPTARG}"; usage 2 1>&2 ;;
esac
;;
*) printf 'Bad option %s\n\n' "${o}"; usage 2 1>&2 ;;
esac
done
shift $((OPTIND-1))
if [[ -z "${1}" ]] && (( ! buildline )); then
usage 2
else
target="${1}"
fi
# Search clipboard-specific SSH key
if [[ -z "${ssh_key}" ]]; then
search_ssh_key=( ~/.ssh/id_*_clipboard )
if [[ -r "${search_ssh_key[0]}" ]]; then
ssh_key="${search_ssh_key[0]}"
printf 'Found key:\t%s\n' "${ssh_key}"
fi
fi
# Build forced command line for the authorized keys config
if (( buildline )); then
echo '# forced command in ~/.ssh/authorized_keys'
if [[ -z "${ssh_key}" ]]; then
echo 'No key available to build a forced-command line' 1>&2
exit 1
fi
echo "# Wayland"
printf "command=\"/usr/bin/env WAYLAND_DISPLAY='wayland-0' wl-copy; echo copied\",no-agent-forwarding,no-pty,no-X11-forwarding %s\n" "$(< "${ssh_key%.pub}.pub" )"
echo "# X11"
printf 'command="xclip -display :0 -in -rmlastnl -selection clipboard; echo copied",no-agent-forwarding,no-pty,no-X11-forwarding %s\n' "$(< "${ssh_key%.pub}.pub" )"
exit 0
fi
# Auto-detect display server
if [[ -n "${gfxsys}" ]]; then
:
elif [[ "${XDG_SESSION_TYPE,,}" =~ wayland|x11 ]]; then
gfxsys="${XDG_SESSION_TYPE,,}"
elif [[ -n "${WAYLAND_DISPLAY}" ]]; then
gfxsys=wayland
echo "Wayland detected"
elif [[ -n "${DISPLAY}" ]]; then
gfxsys=x11
echo "X11 detected"
fi
# Display-specific command
clipcmd=
if [[ "${gfxsys,}" == "x11" ]]; then
clipcmd=( xclip -out -rmlastnl -selection clipboard ) # -display :0
else
clipcmd=( wl-paste --no-newline )
fi
# Copy-Paste time!
echo "Pushing clipboard to ${target} using ${clipcmd[0]} on ${gfxsys^}..."
while read ${read_t:+ "${read_t[@]}" } -r result; do
ssh_pid="$!"
echo -e "${result}"
kill -TERM "${ssh_pid}"
exit 0
done < <(
exec \
${timeout:+ "${timeout[@]}" } \
ssh \
${ssh_options:+ "${ssh_options[@]}" } \
${ssh_key:+ -o"ControlPath none" -o"IdentitiesOnly yes" -i "${ssh_key}"} \
"${target}" \
-- \
/usr/bin/env WAYLAND_DISPLAY="wayland-0" wl-copy \; \
echo copied \
< <( "${clipcmd[@]}" ) \
|| echo "fail ?" >&2
)
echo "timeout" >&2
exit 1
TODO
- [ ] Store the code and the present documentation somewhere better, e.g., pull request into lan-mouse?
@kevinJ045 small improvement suggestion:
content=$(wl-paste -n) for ip in "${ips[@]}"; do echo "$content" | socat - TCP:$ip:$clsport
You paid attentions to avoid trailing lines with -n option of wl-paste... but are then accidentally reintroducing one with echo. I would suggest using echo's -n option:
content="$(wl-paste -n)"
for ip in "${ips[@]}"; do
echo -n "$content" | socat - TCP:$ip:$clsport
also side-note, as you're using bash:
bash has its own network stack with /dev/tcp/ and /dev/udp, so you could also potentially write:
echo -n "${content}" > "/devtcp/${ip}/${clsport}"
Also, FYI, I am trying to gather a collection of what people have implemented as solutions for clipboard sharing, so we can try to properly document it into lan-mouse. I think we could add yours, too.
A very simple way I've found to get clipboard syncing to work is by using https://github.com/quackduck/uniclip.
Once installed, you can run uniclip on one machine and then it will prompt you with the command to run on the other, for example uniclip 10.42.0.200:38815.
One downside I've found with uniclip is that you currently cannot directly target which network interface you want to use, however the network interface it chooses from what I've found is consistent and will always choose the same network interface given the same network configuration.
I would first try running uniclip on both machines and see if either one targets the desired network interface first. Then you can turn off undesired network interfaces until it targets the desired one.
Additionally, you can set the port using the -p flag. That way the command to pair the devices always stays the same, for example uniclip -p 38815 and uniclip 10.42.0.200:38815.
Additionally, I've found that wl-copy and wl-paste, which all wayland clipboard sharing methods use, doesn't work and freezes when in GNOME's overview mode. I switched to hyprland on both devices using end4's dotfiles and it's now working great. Highly recommend if you're looking for an alternative to GNOME.
i let KDE Connect handle the clipboard and it works fine
Note for people who run Plasma 6.5 and use the "focus stealing prevention" options: wl-clipboard is now broken (https://github.com/bugaevc/wl-clipboard/issues/268), you need to switch to package wl-clipboard-rs, at least version 0.9.2.