base on mouse & keyboard sharing via LAN # Lan Mouse Lan Mouse is a *cross-platform* mouse and keyboard sharing software similar to universal-control on Apple devices. It allows for using multiple PCs via a single set of mouse and keyboard. This is also known as a Software KVM switch. Goal of this project is to be an open-source alternative to proprietary tools like [Synergy 2/3](https://symless.com/synergy), [Share Mouse](https://www.sharemouse.com/de/) and other open source tools like [Deskflow](https://github.com/deskflow/deskflow) or [Input Leap](https://github.com/input-leap) (Synergy fork). Focus lies on performance, ease of use and a maintainable implementation that can be expanded to support additional backends for e.g. Android, iOS, ... in the future. ***blazingly fast™*** because it's written in rust. - _Now with a gtk frontend_ <picture> <source media="(prefers-color-scheme: dark)" srcset="/screenshots/dark.png?raw=true"> <source media="(prefers-color-scheme: light)" srcset="/screenshots/light.png?raw=true"> <img alt="Screenshot of Lan-Mouse" srcset="/screenshots/dark.png"> </picture> ## Encryption Lan Mouse encrypts all network traffic using the DTLS implementation provided by [WebRTC.rs](https://github.com/webrtc-rs/webrtc). There are currently no mitigations in place for timing side-channel attacks. ## OS Support Most current desktop environments and operating systems are fully supported, this includes - GNOME >= 45 - KDE Plasma >= 6.1 - Most wlroots based compositors, including Sway (>= 1.8), Hyprland and Wayfire - Windows - MacOS ### Caveats / Known Issues > [!Important] > - **X11** currently only has support for input emulation, i.e. can only be used on the receiving end. > > - **Sway / wlroots**: Wlroots based compositors without libei support on the receiving end currently do not handle modifier events on the client side. > This results in CTRL / SHIFT / ALT / SUPER keys not working with a sending device that is NOT using the `layer-shell` backend > > - **Wayfire**: If you are using [Wayfire](https://github.com/WayfireWM/wayfire), make sure to use a recent version (must be newer than October 23rd) and **add `shortcuts-inhibit` to the list of plugins in your wayfire config!** > Otherwise input capture will not work. > > - **Windows**: The mouse cursor will be invisible when sending input to a Windows system if > there is no real mouse connected to the machine. For more detailed information about os support see [Detailed OS Support](#detailed-os-support) ### Android & IOS A proof of concept for an Android / IOS Application by [rohitsangwan01](https://github.com/rohitsangwan01) can be found [here](https://github.com/rohitsangwan01/lan-mouse-mobile). It can be used as a remote control for any device supported by Lan Mouse. ## Installation <details> <summary>Arch Linux</summary> Lan Mouse can be installed from the [official repositories](https://archlinux.org/packages/extra/x86_64/lan-mouse/): ```sh pacman -S lan-mouse ``` The prerelease version (following `main`) is available on the AUR: ```sh paru -S lan-mouse-git ``` </details> <details> <summary>Nix (OS)</summary> - nixpkgs: [search.nixos.org](https://search.nixos.org/packages?channel=unstable&show=lan-mouse&from=0&size=50&sort=relevance&type=packages&query=lan-mouse) - flake: [README.md](./nix/README.md) </details> <details> <summary>Fedora</summary> You can install Lan Mouse from the [Terra Repository](https://terra.fyralabs.com). After enabling Terra: ```sh dnf install lan-mouse ``` </details> <details> <summary>MacOS</summary> - Download the package for your Mac (Intel or ARM) from the releases page - Unzip it - Remove the quarantine with `xattr -rd com.apple.quarantine "Lan Mouse.app"` - Launch the app - Grant accessibility permissions in System Preferences </details> <details> <summary>Manual Installation</summary> First make sure to [install the necessary dependencies](#installing-dependencies-for-development--compiling-from-source). Precompiled release binaries for Windows, MacOS and Linux are available in the [releases section](https://github.com/feschber/lan-mouse/releases). For Windows, the depenedencies are included in the .zip file, for other operating systems see [Installing Dependencies](#installing-dependencies-for-development--compiling-from-source). Alternatively, the `lan-mouse` binary can be compiled from source (see below). ### Installing desktop file, app icon and firewall rules (optional) ```sh # install lan-mouse (replace path/to/ with the correct path) sudo cp path/to/lan-mouse /usr/local/bin/ # install app icon sudo mkdir -p /usr/local/share/icons/hicolor/scalable/apps sudo cp lan-mouse-gtk/resources/de.feschber.LanMouse.svg /usr/local/share/icons/hicolor/scalable/apps # update icon cache gtk-update-icon-cache /usr/local/share/icons/hicolor/ # install desktop entry sudo mkdir -p /usr/local/share/applications sudo cp de.feschber.LanMouse.desktop /usr/local/share/applications # when using firewalld: install firewall rule sudo cp firewall/lan-mouse.xml /etc/firewalld/services # -> enable the service in firewalld settings ``` Instead of downloading from the releases, the `lan-mouse` binary can be easily compiled via cargo or nix: ### Compiling and installing manually: ```sh # compile in release mode cargo build --release # install lan-mouse sudo cp target/release/lan-mouse /usr/local/bin/ ``` ### Compiling and installing via cargo: ```sh # will end up in ~/.cargo/bin cargo install lan-mouse ``` ### Compiling and installing via nix: ```sh # you can find the executable in result/bin/lan-mouse nix-build ``` ### Conditional compilation Support for other platforms is omitted automatically based on the active rust toolchain. Additionally, available backends and frontends can be configured manually via [cargo features](https://doc.rust-lang.org/cargo/reference/features.html). E.g. if only support for sway is needed, the following command produces an executable with support for only the `layer-shell` capture backend and `wlroots` emulation backend: ```sh cargo build --no-default-features --features layer_shell_capture,wlroots_emulation ``` For a detailed list of available features, checkout the [Cargo.toml](./Cargo.toml) </details> ## Installing Dependencies for Development / Compiling from Source <details> <summary>MacOS</summary> ```sh # Install dependencies brew install libadwaita pkg-config imagemagick cargo install cargo-bundle # Create the macOS icon file scripts/makeicns.sh # Create the .app bundle cargo bundle # Copy all dynamic libraries into the bundle, and update the bundle to find them there scripts/copy-macos-dylib.sh ``` </details> <details> <summary>Ubuntu and derivatives</summary> ```sh sudo apt install libadwaita-1-dev libgtk-4-dev libx11-dev libxtst-dev ``` </details> <details> <summary>Arch and derivatives</summary> ```sh sudo pacman -S libadwaita gtk libx11 libxtst ``` </details> <details> <summary>Fedora and derivatives</summary> ```sh sudo dnf install libadwaita-devel libXtst-devel libX11-devel ``` </details> <details> <summary>Nix</summary> ```sh nix-shell . ``` </details> <details> <summary>Nix (flake)</summary> ```sh nix develop ``` </details> <details> <summary>Windows</summary> - First install [Rust](https://www.rust-lang.org/tools/install). - Then follow the instructions at [gtk-rs.org](https://gtk-rs.org/gtk4-rs/stable/latest/book/installation_windows.html) *TLDR:* Build gtk from source - The following commands should be run in an **admin power shell** instance: ```sh # install chocolatey Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1')) # install gvsbuild dependencies choco install python git msys2 visualstudio2022-workload-vctools ``` - The following commands should be run in a **regular power shell** instance: ```sh # install gvsbuild with python python -m pip install --user pipx python -m pipx ensurepath ``` - Relaunch your powershell instance so the changes in the environment are reflected. ```sh pipx install gvsbuild # build gtk + libadwaita gvsbuild build gtk4 libadwaita librsvg adwaita-icon-theme ``` - **Make sure to add the directory** `C:\gtk-build\gtk\x64\release\bin` [**to the `PATH` environment variable**]((https://learn.microsoft.com/en-us/previous-versions/office/developer/sharepoint-2010/ee537574(v=office.14))). Otherwise the project will fail to build. To avoid building GTK from source, it is possible to disable the gtk frontend (see conditional compilation). </details> ## Usage <details> <summary>Gtk Frontend</summary> By default the gtk frontend will open when running `lan-mouse`. To connect a device you want to control, simply click the `Add` button and enter the hostname of the device. On the *remote* device, authorize your *local* device for incoming traffic using the `Authorize` button under the "Incoming Connections" section. The fingerprint for authorization can be found under the general section of your *local* device. It is of the form "aa:bb:cc:..." Authorized devices can be persisted using the configuration file (see [Configuration](#configuration)). If the device still can not be entered, make sure you have UDP port `4242` (or the one selected) opened up in your firewall. </details> <details> <summary>Command Line Interface</summary> The cli interface can be accessed by passing `cli` as a commandline argument. Use ```sh lan-mouse cli help ``` to list the available commands and ```sh lan-mouse cli <cmd> help ``` for information on how to use a specific command. </details> <details> <summary>Daemon Mode</summary> Lan Mouse can be launched in daemon mode to keep it running in the background (e.g. for use in a systemd-service). To do so, use the `daemon` subcommand: ```sh lan-mouse daemon ``` In order to start lan-mouse with a graphical session automatically, the [systemd-service](service/lan-mouse.service) can be used: Copy the file to `~/.config/systemd/user/` and enable the service: ```sh cp service/lan-mouse.service ~/.config/systemd/user systemctl --user daemon-reload systemctl --user enable --now lan-mouse.service ``` </details> ## Configuration To automatically load clients on startup, the file `$XDG_CONFIG_HOME/lan-mouse/config.toml` is parsed. `$XDG_CONFIG_HOME` defaults to `~/.config/`. To create this file you can copy the following example config: ### Example config > [!TIP] > key symbols in the release bind are named according > to their names in [input-event/src/scancode.rs#L172](input-event/src/scancode.rs#L176). > This is bound to change ```toml # example configuration # configure release bind release_bind = [ "KeyA", "KeyS", "KeyD", "KeyF" ] # optional port (defaults to 4242) port = 4242 # list of authorized tls certificate fingerprints that # are accepted for incoming traffic [authorized_fingerprints] "bc:05:ab:7a:a4:de:88:8c:2f:92:ac:bc:b8:49:b8:24:0d:44:b3:e6:a4:ef:d7:0b:6c:69:6d:77:53:0b:14:80" = "iridium" # define a client on the right side with host name "iridium" [[clients]] # position (left | right | top | bottom) position = "right" # hostname hostname = "iridium" # activate this client immediately when lan-mouse is started activate_on_startup = true # optional list of (known) ip addresses ips = ["192.168.178.156"] # define a client on the left side with IP address 192.168.178.189 [[clients]] position = "left" # The hostname is optional: When no hostname is specified, # at least one ip address needs to be specified. hostname = "thorium" # ips for ethernet and wifi ips = ["192.168.178.189", "192.168.178.172"] # optional port port = 4242 ``` Where `left` can be either `left`, `right`, `top` or `bottom`. ## Roadmap - [x] Graphical frontend (gtk + libadwaita) - [x] respect xdg-config-home for config file location. - [x] IP Address switching - [x] Liveness tracking Automatically ungrab mouse when client unreachable - [x] Liveness tracking: Automatically release keys, when server offline - [x] MacOS KeyCode Translation - [x] Libei Input Capture - [x] MacOS Input Capture - [x] Windows Input Capture - [x] Encryption - [ ] X11 Input Capture - [ ] Latency measurement and visualization - [ ] Bandwidth usage measurement and visualization - [ ] Clipboard support ## Detailed OS Support In order to use a device for sending events, an **input-capture** backend is required, while receiving events requires a supported **input-emulation** *and* **input-capture** backend. A suitable backend is chosen automatically based on the active desktop environment / compositor. The following sections detail the emulation and capture backends provided by lan-mouse and their support in desktop environments / operating systems. ### Input Emulation Support | Desktop / Backend | wlroots | libei | remote-desktop portal | windows | macos | x11 | |---------------------------|--------------------------|--------------------------|--------------------------|--------------------------|----------------------------------------|--------------------| | Wayland (wlroots) | :heavy_check_mark: | | | | | | | Wayland (KDE) | | :heavy_check_mark: | :heavy_check_mark: | | | | | Wayland (Gnome) | | :heavy_check_mark: | :heavy_check_mark: | | | | | Windows | | | | :heavy_check_mark: | | | | MacOS | | | | | :heavy_check_mark: | | | X11 | | | | | | :heavy_check_mark: | - `wlroots`: This backend makes use of the [wlr-virtual-pointer-unstable-v1](https://wayland.app/protocols/wlr-virtual-pointer-unstable-v1) and [virtual-keyboard-unstable-v1](https://wayland.app/protocols/virtual-keyboard-unstable-v1) protocols and is supported by most wlroots based compositors. - `libei`: This backend uses [libei](https://gitlab.freedesktop.org/libinput/libei) and is supported by GNOME >= 45 or KDE Plasma >= 6.1. - `xdp`: This backend uses the [freedesktop remote-desktop-portal](https://flatpak.github.io/xdg-desktop-portal/#gdbus-org.freedesktop.portal.RemoteDesktop) and is supported on GNOME and Plasma. - `x11`: Backend for X11 sessions. - `windows`: Backend for Windows. - `macos`: Backend for MacOS. ### Input Capture Support | Desktop / Backend | layer-shell | libei | windows | macos | x11 | |---------------------------|--------------------------|--------------------------|--------------------------|----------------------------------------|-----| | Wayland (wlroots) | :heavy_check_mark: | | | | | | Wayland (KDE) | :heavy_check_mark: | :heavy_check_mark: | | | | | Wayland (Gnome) | | :heavy_check_mark: | | | | | Windows | | | :heavy_check_mark: | | | | MacOS | | | | :heavy_check_mark: | | | X11 | | | | | WIP | - `layer-shell`: This backend creates a single pixel wide window on the edges of Displays to capture the cursor using the [layer-shell protocol](https://wayland.app/protocols/wlr-layer-shell-unstable-v1). - `libei`: This backend uses [libei](https://gitlab.freedesktop.org/libinput/libei) and is supported by GNOME >= 45 or KDE Plasma >= 6.1. - `windows`: Backend for input capture on Windows. - `macos`: Backend for input capture on MacOS. - `x11`: TODO (not yet supported) ", Assign "at most 3 tags" to the expected json: {"id":"6618","tags":[]} "only from the tags list I provide: [{"id":77,"name":"3d"},{"id":89,"name":"agent"},{"id":17,"name":"ai"},{"id":54,"name":"algorithm"},{"id":24,"name":"api"},{"id":44,"name":"authentication"},{"id":3,"name":"aws"},{"id":27,"name":"backend"},{"id":60,"name":"benchmark"},{"id":72,"name":"best-practices"},{"id":39,"name":"bitcoin"},{"id":37,"name":"blockchain"},{"id":1,"name":"blog"},{"id":45,"name":"bundler"},{"id":58,"name":"cache"},{"id":21,"name":"chat"},{"id":49,"name":"cicd"},{"id":4,"name":"cli"},{"id":64,"name":"cloud-native"},{"id":48,"name":"cms"},{"id":61,"name":"compiler"},{"id":68,"name":"containerization"},{"id":92,"name":"crm"},{"id":34,"name":"data"},{"id":47,"name":"database"},{"id":8,"name":"declarative-gui "},{"id":9,"name":"deploy-tool"},{"id":53,"name":"desktop-app"},{"id":6,"name":"dev-exp-lib"},{"id":59,"name":"dev-tool"},{"id":13,"name":"ecommerce"},{"id":26,"name":"editor"},{"id":66,"name":"emulator"},{"id":62,"name":"filesystem"},{"id":80,"name":"finance"},{"id":15,"name":"firmware"},{"id":73,"name":"for-fun"},{"id":2,"name":"framework"},{"id":11,"name":"frontend"},{"id":22,"name":"game"},{"id":81,"name":"game-engine "},{"id":23,"name":"graphql"},{"id":84,"name":"gui"},{"id":91,"name":"http"},{"id":5,"name":"http-client"},{"id":51,"name":"iac"},{"id":30,"name":"ide"},{"id":78,"name":"iot"},{"id":40,"name":"json"},{"id":83,"name":"julian"},{"id":38,"name":"k8s"},{"id":31,"name":"language"},{"id":10,"name":"learning-resource"},{"id":33,"name":"lib"},{"id":41,"name":"linter"},{"id":28,"name":"lms"},{"id":16,"name":"logging"},{"id":76,"name":"low-code"},{"id":90,"name":"message-queue"},{"id":42,"name":"mobile-app"},{"id":18,"name":"monitoring"},{"id":36,"name":"networking"},{"id":7,"name":"node-version"},{"id":55,"name":"nosql"},{"id":57,"name":"observability"},{"id":46,"name":"orm"},{"id":52,"name":"os"},{"id":14,"name":"parser"},{"id":74,"name":"react"},{"id":82,"name":"real-time"},{"id":56,"name":"robot"},{"id":65,"name":"runtime"},{"id":32,"name":"sdk"},{"id":71,"name":"search"},{"id":63,"name":"secrets"},{"id":25,"name":"security"},{"id":85,"name":"server"},{"id":86,"name":"serverless"},{"id":70,"name":"storage"},{"id":75,"name":"system-design"},{"id":79,"name":"terminal"},{"id":29,"name":"testing"},{"id":12,"name":"ui"},{"id":50,"name":"ux"},{"id":88,"name":"video"},{"id":20,"name":"web-app"},{"id":35,"name":"web-server"},{"id":43,"name":"webassembly"},{"id":69,"name":"workflow"},{"id":87,"name":"yaml"}]" returns me the "expected json"