Real-time Wayland screen mirroring to a terminal or native window using Unicode braille characters, half‑blocks, ASCII, sixels, kitty graphics, framebuffer, KMS direct rendering, native GUI window, or hybrid rendering. Includes bidirectional input forwarding, audio streaming (PipeWire), zooming, focus-follow, and optional NVIDIA CUDA acceleration (server-side).
https://github.com/user-attachments/assets/089d1c60-c502-4422-892c-fb83e392149a
Build (CPU-only)
git clone https://github.com/cyber-wojtek/waytermirror.git
cd waytermirror
meson setup builddir
meson compile -C builddir -j$(nproc)
This produces waytermirror_server and waytermirror_client.
Run the server on your Wayland desktop:
./waytermirror_server
Run the client:
Terminal mode (default - works anywhere):
./waytermirror_client -H <host>
Native GUI window (best experience):
./waytermirror_client -H <host> -R gui
Framebuffer mode (TTY/console - requires root/video group):
sudo ./waytermirror_client -H <host> -R framebuffer
KMS direct rendering (TTY/console - requires root/video group):
sudo ./waytermirror_client -H <host> -R kms
Tip: Run ./waytermirror_client --help or ./waytermirror_server --help to see all available flags and defaults.
sudo pacman -S base-devel git gcc wayland wayland-protocols libinput pipewire lz4 rapidjson systemd argparse libsixel libpng ffmpeg opus libdrm mesa meson
# Optional for CUDA:
sudo pacman -S cuda
sudo apt install build-essential git gcc libwayland-dev wayland-protocols libinput-dev libpipewire-0.3-dev liblz4-dev rapidjson-dev libsystemd-dev pkg-config meson libargparse-dev libsixel-dev libpng-dev libavcodec-dev libavutil-dev libswscale-dev libavformat-dev libopus-dev libudev-dev libdrm-dev libgbm-dev
# For CUDA: install NVIDIA CUDA toolkit from https://developer.nvidia.com/cuda-toolkit
meson setup builddir
meson compile -C builddir -j$(nproc)
meson setup builddir -Dnvidia_cuda=true
meson compile -C builddir -j$(nproc)
WAYTERMIRROR_CUDA=1 or WAYTERMIRROR_NO_CUDA.waytermirror_serverwaytermirror_clientAUR: Install directly from the waytermirror-git AUR package:
yay -S waytermirror-git
# or
paru -S waytermirror-git
Manual build: Use the included PKGBUILD with makepkg:
mkdir -p waytermirror
cd waytermirror
wget https://github.com/cyber-wojtek/waytermirror/raw/refs/heads/main/PKGBUILD
makepkg -si
input group or run with sufficient privileges to read /dev/input/*)./dev/fb0 and /dev/dri/card*../waytermirror_server [options]
For the authoritative list use
./waytermirror_server --help.
| Flag / Short | Long / Name | Description | Default |
|---|---|---|---|
| -P | --port | Base TCP port (video base; other services use base+N) | 9999 |
| -F | --capture-fps | Capture framerate from compositor | 30 |
| -R | --capture-resolution | Capture resolution: auto or WxH (e.g., 1920x1080) | auto |
| -C | --compositor <auto|hyprland|sway|kde|gnome|generic> | Compositor override | auto |
| -B | --capture-backend <auto|wlr|pipewire> | Screen capture backend | auto |
| -I | --input-backend <auto|virtual|uinput> | Input injection backend | auto |
| -n | --no-video | Disable screen capture / video streaming | off |
| -A | --no-audio | Disable system audio streaming | off |
| -N | --no-input | Disable input injection (do not create virtual devices) | off |
| -m | --no-microphone | Disable microphone reception (client→server mic) | off |
Capture backend notes:
wlr: Uses wlr-screencopy protocol directly (compositor must support wlr-screencopy-unstable-v1)pipewire: Uses PipeWire + xdg-desktop-portal for screen capture (works on more compositors including GNOME/KDE)
-o option.auto: Automatically detects and prefers wlr-screencopy if available, falls back to PipeWireInput backend notes:
virtual: Uses Wayland virtual input protocols (zwlr_virtual_pointer_v1, zwp_virtual_keyboard_v1) — requires compositor supportuinput: Uses Linux uinput (/dev/uinput) — works on any compositor but requires proper permissionsauto: Automatically selects virtual protocols if available, falls back to uinput./waytermirror_client -H <server_ip> [options]
For the authoritative list use
./waytermirror_client --help.
Connection
| Flag / Short | Long / Name | Description | Default |
|---|---|---|---|
| -H | --host | Server IP/hostname (required) | required |
| -P | --port | Server base port | 9999 |
Video & rendering
| Flag / Short | Long / Name | Description | Default |
|---|---|---|---|
| -o <n|follow> | --output <n|follow> | Output index or follow to track focused window | 0 |
| -F | --fps | Target client FPS / playback framerate | 30 |
| -M <16|256|true> | --mode <16|256|true> | Color mode (16, 256, truecolor) | 256 |
| -R | --renderer <braille|blocks|ascii|sixel|kitty|framebuffer|kms|gui|hybrid> | Rendering method | braille |
| -K | --receive-resolution | Client-side decode resolution: native or WxH | native |
| -r <cpu|cuda> | --render-device <cpu|cuda> | Prefer server-side renderer (for Unicode modes) | cpu |
| -d <0-100> | --detail-level <0-100> | Visual detail (0: fast/smooth, 100: sharp) | 50 |
| -Q <0-100> | --quality <0-100> | H.265 encoding quality (0: fast/low, 100: slow/best) | 50 |
| -S | --scale | Scale factor for rendered output | 1.0 |
| -k | --keep-aspect-ratio | Maintain aspect ratio when scaling | off |
| -c | --compress | Enable LZ4 compression (for Unicode renderers) | off |
| -L <0-12> | --compression-level <0-12> | LZ4 HC level (0=fast, 12=best) | 0 |
| -T | --rotation | Rotation angle (0-360 degrees) | 0 |
| -n | --no-video | Disable video display | off |
Renderer descriptions:
/dev/fb0) rendering (requires root, works on TTY)Quality setting notes:
Input (local client input capture / forwarding)
| Flag / Short | Long / Name | Description | Default |
|---|---|---|---|
| -N | --no-input | Do not capture/forward local input | off |
| -x | --exclusive-input | Grab input devices exclusively (EVIOCGRAB) | off |
| -C | --center-mouse | Start mouse at screen center when connecting | off |
Audio
| Flag / Short | Long / Name | Description | Default |
|---|---|---|---|
| -A | --no-audio | Disable audio playback (server→client) | off |
| -a | --audio-compress | Enable audio Opus compression | off |
| -u | --audio-sample-rate | Audio opus sample rate | 48000 |
| -v | --audio-channels | Audio opus channels | 2 |
| -b | --audio-bitrate | Audio opus bitrate | 64 |
| -y <0-10> | --audio-complexity <0-10> | Opus audio complexity | 5 |
| -w | --audio-application <voip|audio|lowdelay> | Opus audio application mode | audio |
| -p | --no-microphone | Disable microphone capture (client→server) | off |
| -m | --microphone-compress | Enable microphone Opus compression | off |
| -U | --microphone-sample-rate | Microphone opus sample rate | 48000 |
| -V | --microphone-channels | Microphone opus channels | 2 |
| -B | --microphone-bitrate | Microphone opus bitrate | 64 |
| -Y <0-10> | --microphone-complexity <0-10> | Microphon- See CONTRIBUTING.md for guidelines | |
| e opus complexity | 5 | ||
| -W | --microphone-application <voip|audio|lowdelay> | Microphone opus application mode | voip |
Zoom / viewport
| Flag / Short | Long / Name | Description | Default |
|---|---|---|---|
| -z | --zoom | Start with zoom enabled | off |
| -Z <1-10> | --zoom-level <1-10> | Magnification | 2.0 |
| -X | --zoom-width | Viewport width (px) | 800 |
| -Y | --zoom-height | Viewport height (px) | 600 |
| -f | --zoom-follow | Follow mouse while zoomed | on |
| -s | --zoom-smooth | Smooth panning while zoomed | on |
| -D | --zoom-speed | Pan speed (px/frame) | 20 |
Basic LAN streaming (terminal)
# Server (desktop)
./waytermirror_server -F 60
# Client (terminal)
./waytermirror_client -H 192.168.1.100 -F 60 -M true -R hybrid
Native GUI window (best experience for graphical applications)
./waytermirror_client -H 192.168.1.100 -R gui -Q 75
# Creates a native Wayland window, resizable, hardware-accelerated
# No terminal required, works like a native application
High quality sixel with hardware encoding
./waytermirror_client -H 192.168.1.100 -R sixel -Q 85 -M true
# Server auto-detects best hardware encoder (NVENC/QuickSync/VAAPI/AMF)
Framebuffer output (TTY/console)
sudo ./waytermirror_client -H 192.168.1.100 -R framebuffer -Q 70
# Mirrors to /dev/fb0, hardware-accelerated H.265 encoding
KMS direct rendering (best TTY performance)
sudo ./waytermirror_client -H 192.168.1.100 -R kms -Q 80
# Direct KMS/DRM rendering with hardware acceleration
Low bandwidth (Unicode with compression)
./waytermirror_client -H server.example.com -c -L 12 -F 15 -M 256 -d 30 -R braille
Follow focused window
./waytermirror_client -H 192.168.1.100 -o follow
Input-only (control remote desktop without video)
./waytermirror_client -H 192.168.1.100 -n -x
4K GUI window with audio
./waytermirror_client -H 192.168.1.100 -R gui -K 3840x2160 -Q 85 -a -b 128
Zoom mode with GUI
./waytermirror_client -H 192.168.1.100 -R gui -z -Z 3.0 -X 1920 -Y 1080
All client shortcuts use the Ctrl+Alt+Shift modifier prefix, so normal keys are forwarded to the remote session. Press Ctrl+Alt+Shift+H at any time to display the full shortcut list with current toggle states.
| Shortcut | Action | Notes |
|---|---|---|
| Ctrl+Alt+Shift+Q | Quit / disconnect | Graceful disconnect (sends close to server) |
| Ctrl+Alt+Shift+H | Toggle help | Display all shortcuts and current state |
| Ctrl+Alt+Shift+P | Pause / resume video | Stops rendering updates locally (input still forwarded) |
| Shortcut | Action | Notes |
|---|---|---|
| Ctrl+Alt+Shift+I | Toggle input forwarding | Enable/disable forwarding of keyboard & mouse to server |
| Ctrl+Alt+Shift+G | Toggle exclusive grab | EVIOCGRAB on local devices (when supported) |
| Shortcut | Action | Notes |
|---|---|---|
| Ctrl+Alt+Shift+Z | Toggle zoom mode | When zoomed, use arrow keys to pan |
| Ctrl+Alt+Shift++ (or =) | Zoom in | Increases zoom level by 0.125× |
| Ctrl+Alt+Shift+- | Zoom out | Decreases zoom level by 0.125× |
| Ctrl+Alt+Shift+0 | Reset zoom | Reset to 2.0× and center viewport |
| Ctrl+Alt+Shift+N | Toggle zoom follow | Enable/disable zoom following mouse cursor |
| Ctrl+Alt+Shift+Arrow keys | Pan viewport | Left/Right/Up/Down — uses configured pan speed |
| Ctrl+Alt+Shift+PageUp/PageDown | Fast vertical pan | 5× normal pan speed |
| Ctrl+Alt+Shift+Home/End | Fast horizontal pan | 5× normal pan speed |
| Shortcut | Action | Notes |
|---|---|---|
| Ctrl+Alt+Shift+[ | Rotate left 5° | Counter-clockwise rotation |
| Ctrl+Alt+Shift+] | Rotate right 5° | Clockwise rotation |
| Ctrl+Alt+Shift+\ | Reset rotation | Return to 0° |
| Ctrl+Alt+Shift+T | Rotate 90° CW | Quick 90° clockwise rotation |
| Ctrl+Alt+Shift+Y | Rotate 90° CCW | Quick 90° counter-clockwise rotation |
| Shortcut | Action | Notes |
|---|---|---|
| Ctrl+Alt+Shift+R | Cycle renderer | braille → blocks → ascii → hybrid → sixel → kitty → framebuffer → kms → gui |
| Ctrl+Alt+Shift+C | Cycle color mode | 16 → 256 → truecolor |
| Ctrl+Alt+Shift+D | Increase detail | +10 detail level (Unicode) or quality (H.265) |
| Ctrl+Alt+Shift+S | Decrease detail | −10 detail level (Unicode) or quality (H.265) |
| Ctrl+Alt+Shift+W | Increase quality | +10 quality level |
| Ctrl+Alt+Shift+E | Decrease quality | −10 quality level |
| Ctrl+Alt+Shift+O | Toggle smooth panning | Enable/disable smooth zoom panning |
| Ctrl+Alt+Shift+B | Toggle aspect ratio | Keep/ignore aspect ratio when scaling |
| Ctrl+Alt+Shift+V | Cycle render device | CPU → CUDA (for Unicode modes) |
| Ctrl+Alt+Shift+U | Toggle compression | Enable/disable LZ4 compression (Unicode modes) |
| Ctrl+Alt+Shift+L | Cycle compression level | Off → fast LZ4 → HC levels (Unicode modes) |
| Shortcut | Action | Notes |
|---|---|---|
| Ctrl+Alt+Shift+` | Cycle output | Next output or toggle follow-focus |
| Ctrl+Alt+Shift+J | Increase FPS | +5 FPS |
| Ctrl+Alt+Shift+K | Decrease FPS | −5 FPS (min 0) |
| Ctrl+Alt+Shift+F | Toggle focus-follow | Follow focused output/window |
| Shortcut | Action | Notes |
|---|---|---|
| Ctrl+Alt+Shift+A | Toggle audio | Mute/unmute system audio playback |
| Ctrl+Alt+Shift+M | Toggle microphone | Mute/unmute microphone capture |
| Ctrl+Alt+Shift+5 | Cycle audio compression | Off → Opus |
| Ctrl+Alt+Shift+6 | Cycle microphone compression | Off → Opus |
Default base port is 9999 (see -P / --port).
Native GUI window (recommended)
-R gui -Q 70
Sixel mode
-R sixel -Q 70
Kitty mode
-R kitty -Q 70
Framebuffer mode
sudo -R framebuffer -Q 80
KMS mode (best TTY performance)
sudo -R kms -Q 85
Quality ranges:
Hardware encoder auto-detection priority:
Maximum quality
-r cuda -R braille -d 100 -Q 100 -M true
Smooth video
-R hybrid -d 30 -F 60
Low bandwidth
-c -L 9 -M 256 -F 15 -d 30
Low latency
-F 60 -Q 0 -d 50
"Failed to initialize libinput"
sudo usermod -aG input $USER
# Log out and back in
PipeWire issues (audio not working)
systemctl --user status pipewire
systemctl --user restart pipewire
CUDA errors / GPU verification
nvcc --version
nvidia-smi
H.265 encoding issues
intel-media-driver on Arch)Sixel/Kitty/GUI rendering artifacts
-Q 80 or higher-R gui for native windowGUI window not responding
-R sixel or -R kmsGUI window won't resize
-K WxH to set specific resolutionPermissions to /dev/input
# Check group membership
groups $USER
# Add to input group if missing
sudo usermod -aG input $USER
Framebuffer/KMS permission denied
# Framebuffer
sudo chmod 666 /dev/fb0 # Temporary
# Or run with sudo
# KMS/DRM
sudo usermod -aG video $USER # Permanent
# Log out and back in
Logs & debugging
Security considerations
ssh -L 9999:localhost:9999 -L 10000:localhost:10000 -L 10001:localhost:10001 \
-L 10002:localhost:10002 -L 10003:localhost:10003 user@server
# Then connect client to localhost
Limitations
input group membershipRecommended setup
Unicode modes (braille/blocks/ascii/hybrid)
Pixel modes (sixel/kitty/framebuffer/kms/gui)
Server (encoding)
Client (decoding)
Unicode rendering (optional)
CUDA=trueUnicode modes
Pixel modes
-Q flagAudio
MIT License — see LICENSE file for details.
Project Status: Active development
318 commits
Hacker News (1)
C++
71.7%
C
23.4%
Cuda
3.6%
Meson
1.0%
Real-time Wayland screen mirroring to a terminal or native window using Unicode braille characters, half‑blocks, ASCII, sixels, kitty graphics, framebuffer, KMS direct rendering, native GUI window, or hybrid rendering. Includes bidirectional input forwarding, audio streaming (PipeWire), zooming, focus-follow, and optional NVIDIA CUDA acceleration (server-side).
https://github.com/user-attachments/assets/089d1c60-c502-4422-892c-fb83e392149a
Build (CPU-only)
git clone https://github.com/cyber-wojtek/waytermirror.git
cd waytermirror
meson setup builddir
meson compile -C builddir -j$(nproc)
This produces waytermirror_server and waytermirror_client.
Run the server on your Wayland desktop:
./waytermirror_server
Run the client:
Terminal mode (default - works anywhere):
./waytermirror_client -H <host>
Native GUI window (best experience):
./waytermirror_client -H <host> -R gui
Framebuffer mode (TTY/console - requires root/video group):
sudo ./waytermirror_client -H <host> -R framebuffer
KMS direct rendering (TTY/console - requires root/video group):
sudo ./waytermirror_client -H <host> -R kms
Tip: Run ./waytermirror_client --help or ./waytermirror_server --help to see all available flags and defaults.
sudo pacman -S base-devel git gcc wayland wayland-protocols libinput pipewire lz4 rapidjson systemd argparse libsixel libpng ffmpeg opus libdrm mesa meson
# Optional for CUDA:
sudo pacman -S cuda
sudo apt install build-essential git gcc libwayland-dev wayland-protocols libinput-dev libpipewire-0.3-dev liblz4-dev rapidjson-dev libsystemd-dev pkg-config meson libargparse-dev libsixel-dev libpng-dev libavcodec-dev libavutil-dev libswscale-dev libavformat-dev libopus-dev libudev-dev libdrm-dev libgbm-dev
# For CUDA: install NVIDIA CUDA toolkit from https://developer.nvidia.com/cuda-toolkit
meson setup builddir
meson compile -C builddir -j$(nproc)
meson setup builddir -Dnvidia_cuda=true
meson compile -C builddir -j$(nproc)
WAYTERMIRROR_CUDA=1 or WAYTERMIRROR_NO_CUDA.waytermirror_serverwaytermirror_clientAUR: Install directly from the waytermirror-git AUR package:
yay -S waytermirror-git
# or
paru -S waytermirror-git
Manual build: Use the included PKGBUILD with makepkg:
mkdir -p waytermirror
cd waytermirror
wget https://github.com/cyber-wojtek/waytermirror/raw/refs/heads/main/PKGBUILD
makepkg -si
input group or run with sufficient privileges to read /dev/input/*)./dev/fb0 and /dev/dri/card*../waytermirror_server [options]
For the authoritative list use
./waytermirror_server --help.
| Flag / Short | Long / Name | Description | Default |
|---|---|---|---|
| -P | --port | Base TCP port (video base; other services use base+N) | 9999 |
| -F | --capture-fps | Capture framerate from compositor | 30 |
| -R | --capture-resolution | Capture resolution: auto or WxH (e.g., 1920x1080) | auto |
| -C | --compositor <auto|hyprland|sway|kde|gnome|generic> | Compositor override | auto |
| -B | --capture-backend <auto|wlr|pipewire> | Screen capture backend | auto |
| -I | --input-backend <auto|virtual|uinput> | Input injection backend | auto |
| -n | --no-video | Disable screen capture / video streaming | off |
| -A | --no-audio | Disable system audio streaming | off |
| -N | --no-input | Disable input injection (do not create virtual devices) | off |
| -m | --no-microphone | Disable microphone reception (client→server mic) | off |
Capture backend notes:
wlr: Uses wlr-screencopy protocol directly (compositor must support wlr-screencopy-unstable-v1)pipewire: Uses PipeWire + xdg-desktop-portal for screen capture (works on more compositors including GNOME/KDE)
-o option.auto: Automatically detects and prefers wlr-screencopy if available, falls back to PipeWireInput backend notes:
virtual: Uses Wayland virtual input protocols (zwlr_virtual_pointer_v1, zwp_virtual_keyboard_v1) — requires compositor supportuinput: Uses Linux uinput (/dev/uinput) — works on any compositor but requires proper permissionsauto: Automatically selects virtual protocols if available, falls back to uinput./waytermirror_client -H <server_ip> [options]
For the authoritative list use
./waytermirror_client --help.
Connection
| Flag / Short | Long / Name | Description | Default |
|---|---|---|---|
| -H | --host | Server IP/hostname (required) | required |
| -P | --port | Server base port | 9999 |
Video & rendering
| Flag / Short | Long / Name | Description | Default |
|---|---|---|---|
| -o <n|follow> | --output <n|follow> | Output index or follow to track focused window | 0 |
| -F | --fps | Target client FPS / playback framerate | 30 |
| -M <16|256|true> | --mode <16|256|true> | Color mode (16, 256, truecolor) | 256 |
| -R | --renderer <braille|blocks|ascii|sixel|kitty|framebuffer|kms|gui|hybrid> | Rendering method | braille |
| -K | --receive-resolution | Client-side decode resolution: native or WxH | native |
| -r <cpu|cuda> | --render-device <cpu|cuda> | Prefer server-side renderer (for Unicode modes) | cpu |
| -d <0-100> | --detail-level <0-100> | Visual detail (0: fast/smooth, 100: sharp) | 50 |
| -Q <0-100> | --quality <0-100> | H.265 encoding quality (0: fast/low, 100: slow/best) | 50 |
| -S | --scale | Scale factor for rendered output | 1.0 |
| -k | --keep-aspect-ratio | Maintain aspect ratio when scaling | off |
| -c | --compress | Enable LZ4 compression (for Unicode renderers) | off |
| -L <0-12> | --compression-level <0-12> | LZ4 HC level (0=fast, 12=best) | 0 |
| -T | --rotation | Rotation angle (0-360 degrees) | 0 |
| -n | --no-video | Disable video display | off |
Renderer descriptions:
/dev/fb0) rendering (requires root, works on TTY)Quality setting notes:
Input (local client input capture / forwarding)
| Flag / Short | Long / Name | Description | Default |
|---|---|---|---|
| -N | --no-input | Do not capture/forward local input | off |
| -x | --exclusive-input | Grab input devices exclusively (EVIOCGRAB) | off |
| -C | --center-mouse | Start mouse at screen center when connecting | off |
Audio
| Flag / Short | Long / Name | Description | Default |
|---|---|---|---|
| -A | --no-audio | Disable audio playback (server→client) | off |
| -a | --audio-compress | Enable audio Opus compression | off |
| -u | --audio-sample-rate | Audio opus sample rate | 48000 |
| -v | --audio-channels | Audio opus channels | 2 |
| -b | --audio-bitrate | Audio opus bitrate | 64 |
| -y <0-10> | --audio-complexity <0-10> | Opus audio complexity | 5 |
| -w | --audio-application <voip|audio|lowdelay> | Opus audio application mode | audio |
| -p | --no-microphone | Disable microphone capture (client→server) | off |
| -m | --microphone-compress | Enable microphone Opus compression | off |
| -U | --microphone-sample-rate | Microphone opus sample rate | 48000 |
| -V | --microphone-channels | Microphone opus channels | 2 |
| -B | --microphone-bitrate | Microphone opus bitrate | 64 |
| -Y <0-10> | --microphone-complexity <0-10> | Microphon- See CONTRIBUTING.md for guidelines | |
| e opus complexity | 5 | ||
| -W | --microphone-application <voip|audio|lowdelay> | Microphone opus application mode | voip |
Zoom / viewport
| Flag / Short | Long / Name | Description | Default |
|---|---|---|---|
| -z | --zoom | Start with zoom enabled | off |
| -Z <1-10> | --zoom-level <1-10> | Magnification | 2.0 |
| -X | --zoom-width | Viewport width (px) | 800 |
| -Y | --zoom-height | Viewport height (px) | 600 |
| -f | --zoom-follow | Follow mouse while zoomed | on |
| -s | --zoom-smooth | Smooth panning while zoomed | on |
| -D | --zoom-speed | Pan speed (px/frame) | 20 |
Basic LAN streaming (terminal)
# Server (desktop)
./waytermirror_server -F 60
# Client (terminal)
./waytermirror_client -H 192.168.1.100 -F 60 -M true -R hybrid
Native GUI window (best experience for graphical applications)
./waytermirror_client -H 192.168.1.100 -R gui -Q 75
# Creates a native Wayland window, resizable, hardware-accelerated
# No terminal required, works like a native application
High quality sixel with hardware encoding
./waytermirror_client -H 192.168.1.100 -R sixel -Q 85 -M true
# Server auto-detects best hardware encoder (NVENC/QuickSync/VAAPI/AMF)
Framebuffer output (TTY/console)
sudo ./waytermirror_client -H 192.168.1.100 -R framebuffer -Q 70
# Mirrors to /dev/fb0, hardware-accelerated H.265 encoding
KMS direct rendering (best TTY performance)
sudo ./waytermirror_client -H 192.168.1.100 -R kms -Q 80
# Direct KMS/DRM rendering with hardware acceleration
Low bandwidth (Unicode with compression)
./waytermirror_client -H server.example.com -c -L 12 -F 15 -M 256 -d 30 -R braille
Follow focused window
./waytermirror_client -H 192.168.1.100 -o follow
Input-only (control remote desktop without video)
./waytermirror_client -H 192.168.1.100 -n -x
4K GUI window with audio
./waytermirror_client -H 192.168.1.100 -R gui -K 3840x2160 -Q 85 -a -b 128
Zoom mode with GUI
./waytermirror_client -H 192.168.1.100 -R gui -z -Z 3.0 -X 1920 -Y 1080
All client shortcuts use the Ctrl+Alt+Shift modifier prefix, so normal keys are forwarded to the remote session. Press Ctrl+Alt+Shift+H at any time to display the full shortcut list with current toggle states.
| Shortcut | Action | Notes |
|---|---|---|
| Ctrl+Alt+Shift+Q | Quit / disconnect | Graceful disconnect (sends close to server) |
| Ctrl+Alt+Shift+H | Toggle help | Display all shortcuts and current state |
| Ctrl+Alt+Shift+P | Pause / resume video | Stops rendering updates locally (input still forwarded) |
| Shortcut | Action | Notes |
|---|---|---|
| Ctrl+Alt+Shift+I | Toggle input forwarding | Enable/disable forwarding of keyboard & mouse to server |
| Ctrl+Alt+Shift+G | Toggle exclusive grab | EVIOCGRAB on local devices (when supported) |
| Shortcut | Action | Notes |
|---|---|---|
| Ctrl+Alt+Shift+Z | Toggle zoom mode | When zoomed, use arrow keys to pan |
| Ctrl+Alt+Shift++ (or =) | Zoom in | Increases zoom level by 0.125× |
| Ctrl+Alt+Shift+- | Zoom out | Decreases zoom level by 0.125× |
| Ctrl+Alt+Shift+0 | Reset zoom | Reset to 2.0× and center viewport |
| Ctrl+Alt+Shift+N | Toggle zoom follow | Enable/disable zoom following mouse cursor |
| Ctrl+Alt+Shift+Arrow keys | Pan viewport | Left/Right/Up/Down — uses configured pan speed |
| Ctrl+Alt+Shift+PageUp/PageDown | Fast vertical pan | 5× normal pan speed |
| Ctrl+Alt+Shift+Home/End | Fast horizontal pan | 5× normal pan speed |
| Shortcut | Action | Notes |
|---|---|---|
| Ctrl+Alt+Shift+[ | Rotate left 5° | Counter-clockwise rotation |
| Ctrl+Alt+Shift+] | Rotate right 5° | Clockwise rotation |
| Ctrl+Alt+Shift+\ | Reset rotation | Return to 0° |
| Ctrl+Alt+Shift+T | Rotate 90° CW | Quick 90° clockwise rotation |
| Ctrl+Alt+Shift+Y | Rotate 90° CCW | Quick 90° counter-clockwise rotation |
| Shortcut | Action | Notes |
|---|---|---|
| Ctrl+Alt+Shift+R | Cycle renderer | braille → blocks → ascii → hybrid → sixel → kitty → framebuffer → kms → gui |
| Ctrl+Alt+Shift+C | Cycle color mode | 16 → 256 → truecolor |
| Ctrl+Alt+Shift+D | Increase detail | +10 detail level (Unicode) or quality (H.265) |
| Ctrl+Alt+Shift+S | Decrease detail | −10 detail level (Unicode) or quality (H.265) |
| Ctrl+Alt+Shift+W | Increase quality | +10 quality level |
| Ctrl+Alt+Shift+E | Decrease quality | −10 quality level |
| Ctrl+Alt+Shift+O | Toggle smooth panning | Enable/disable smooth zoom panning |
| Ctrl+Alt+Shift+B | Toggle aspect ratio | Keep/ignore aspect ratio when scaling |
| Ctrl+Alt+Shift+V | Cycle render device | CPU → CUDA (for Unicode modes) |
| Ctrl+Alt+Shift+U | Toggle compression | Enable/disable LZ4 compression (Unicode modes) |
| Ctrl+Alt+Shift+L | Cycle compression level | Off → fast LZ4 → HC levels (Unicode modes) |
| Shortcut | Action | Notes |
|---|---|---|
| Ctrl+Alt+Shift+` | Cycle output | Next output or toggle follow-focus |
| Ctrl+Alt+Shift+J | Increase FPS | +5 FPS |
| Ctrl+Alt+Shift+K | Decrease FPS | −5 FPS (min 0) |
| Ctrl+Alt+Shift+F | Toggle focus-follow | Follow focused output/window |
| Shortcut | Action | Notes |
|---|---|---|
| Ctrl+Alt+Shift+A | Toggle audio | Mute/unmute system audio playback |
| Ctrl+Alt+Shift+M | Toggle microphone | Mute/unmute microphone capture |
| Ctrl+Alt+Shift+5 | Cycle audio compression | Off → Opus |
| Ctrl+Alt+Shift+6 | Cycle microphone compression | Off → Opus |
Default base port is 9999 (see -P / --port).
Native GUI window (recommended)
-R gui -Q 70
Sixel mode
-R sixel -Q 70
Kitty mode
-R kitty -Q 70
Framebuffer mode
sudo -R framebuffer -Q 80
KMS mode (best TTY performance)
sudo -R kms -Q 85
Quality ranges:
Hardware encoder auto-detection priority:
Maximum quality
-r cuda -R braille -d 100 -Q 100 -M true
Smooth video
-R hybrid -d 30 -F 60
Low bandwidth
-c -L 9 -M 256 -F 15 -d 30
Low latency
-F 60 -Q 0 -d 50
"Failed to initialize libinput"
sudo usermod -aG input $USER
# Log out and back in
PipeWire issues (audio not working)
systemctl --user status pipewire
systemctl --user restart pipewire
CUDA errors / GPU verification
nvcc --version
nvidia-smi
H.265 encoding issues
intel-media-driver on Arch)Sixel/Kitty/GUI rendering artifacts
-Q 80 or higher-R gui for native windowGUI window not responding
-R sixel or -R kmsGUI window won't resize
-K WxH to set specific resolutionPermissions to /dev/input
# Check group membership
groups $USER
# Add to input group if missing
sudo usermod -aG input $USER
Framebuffer/KMS permission denied
# Framebuffer
sudo chmod 666 /dev/fb0 # Temporary
# Or run with sudo
# KMS/DRM
sudo usermod -aG video $USER # Permanent
# Log out and back in
Logs & debugging
Security considerations
ssh -L 9999:localhost:9999 -L 10000:localhost:10000 -L 10001:localhost:10001 \
-L 10002:localhost:10002 -L 10003:localhost:10003 user@server
# Then connect client to localhost
Limitations
input group membershipRecommended setup
Unicode modes (braille/blocks/ascii/hybrid)
Pixel modes (sixel/kitty/framebuffer/kms/gui)
Server (encoding)
Client (decoding)
Unicode rendering (optional)
CUDA=trueUnicode modes
Pixel modes
-Q flagAudio
MIT License — see LICENSE file for details.
Project Status: Active development
Hacker News (1)
318 commits
C++
71.7%
C
23.4%
Cuda
3.6%
Meson
1.0%