Installation¶
JuhRadial MX builds from source on every supported distribution. The fastest path is the one-line installer, which detects your distro, pulls the right packages, compiles the Rust daemon, and wires up autostart and device permissions for you. Manual and NixOS paths are documented below for people who prefer to drive each step themselves.
Note
JuhRadial MX targets Wayland first (KDE Plasma 6, GNOME, Hyprland, COSMIC, Sway, niri) and also runs on X11. See Compositor-Support for per-compositor behaviour, and Features for what you get once it is running.
Contents¶
- Quick start: the one-line installer
- What the installer does
- Bazzite, Fedora Atomic and other image-based systems
- Requirements
- Manual installation per distro
- Fedora
- Ubuntu / Debian
- Arch Linux
- openSUSE Tumbleweed
- NixOS (flake)
- Build from source
- Updating
- After installing
Quick start: the one-line installer¶
The script is interactive: it prints a summary of your system (distro, kernel, session, desktop, whether a Logitech receiver is detected) and asks you to confirm before it changes anything.
Warning
Do not run the installer as root or under sudo. It refuses to run as root and asks for sudo only on the individual steps that need it (package install, copying files into /usr/local and /usr/share, installing udev rules). The systemd service and config live in your user account, so it must run as your normal user.
What the installer does¶
The installer (install.sh) runs as a fresh install or an in-place upgrade, depending on whether /opt/juhradial-mx already exists. A full run has six visible steps plus a preflight phase.
| Phase | What happens |
|---|---|
| Detect | Reads /etc/os-release to resolve a distro family (arch, fedora, debian, opensuse), falling back to ID_LIKE and then to the available package manager (pacman, apt-get, dnf, zypper). Detects Wayland vs X11, the desktop/compositor, an existing install, and whether a Logitech device (USB vendor 046d) is present. |
| 1. Dependencies | Installs the build and runtime packages for your distro family (Rust/Cargo, Python + PyQt6, GTK4/libadwaita, gtk4-layer-shell, HID and evdev dev headers, D-Bus/systemd dev headers, ydotool, git, make). |
| 2. Fetch source | Downloads this version's release tarball (a source snapshot plus a prebuilt daemon) and checks it against the SHA256SUMS published with the release. Without a release, or when the tarball does not match, it clones https://github.com/JuhLabs/juhradial-mx into /opt/juhradial-mx instead (on upgrade: git fetch + git reset --hard origin/master + git clean -fd). |
| 3. Build daemon | Uses the release's prebuilt daemon when it runs on this system; otherwise ensures a usable Rust toolchain (see the rustup note below) and runs cargo build --release in daemon/. |
| 4. Install files | Copies the daemon binary, overlay scripts, theme/device assets, launcher scripts, desktop entries, icons, the systemd user unit, and udev rules into place (see layout table). |
| 5. Desktop integration | Applies compositor-specific setup (Hyprland window rules, GNOME cursor-helper extension). KDE, Sway, and COSMIC need no extra config. |
| 6. Enable service | Reloads the systemd user manager, enables juhradialmx-daemon, sets up ydotoold autostart, then starts (or restarts) the daemon. |
Install layout¶
| Path | Contents |
|---|---|
/opt/juhradial-mx |
Cloned source tree (used for updates) |
/usr/local/bin/juhradiald |
Compiled Rust daemon |
/usr/local/bin/juhradial-mx, /usr/local/bin/juhradial-settings |
Launcher scripts |
/usr/share/juhradial/ |
Overlay Python, Flow module, locales, theme and device assets |
/usr/share/applications/ |
.desktop entries for the app and settings |
~/.config/systemd/user/juhradialmx-daemon.service |
systemd user service |
~/.config/juhradial/ |
Your config.json (see Configuration) |
udev rules, uinput, and the input group¶
For non-root access to the mouse and for action injection, the installer:
- Installs
99-juhradialmx.rulesinto/etc/udev/rules.d/and removes the older99-logitech-hidpp.rulesif present. - Installs
70-juhradialmx-uaccess.rules, which gives the logged-in user access to Logitech hidraw and input nodes as soon as they appear (systemd-loginduaccess), so the MX devices work right after installing and after every Bluetooth reconnect, without logging out. - Installs
60-ydotool-uinput.rulesso the logged-in user can open/dev/uinput(used by bothydotooland the daemon's own virtual input device). Stock distros ship/dev/uinputasroot:root 0600. - Loads the
uinputkernel module now (modprobe uinput) and on boot (/etc/modules-load.d/juhradial-uinput.conf). - Reloads rules (
udevadm control --reload-rules+udevadm trigger). - Ensures the
inputgroup exists and adds your user to it. The group covers what the uaccess rule does not: devices of other makers, which macro recording and generic-mode mice read, and sessions that are not on the local seat.
Info
Your MX devices work right away through the uaccess rule. If your user was just added to the input group, log out and back in once for that too: until then macro recording and generic-mode mice cannot open devices of other makers. juhradial-mx doctor shows which applies.
ydotoold (Wayland action injection)¶
Keyboard-shortcut button actions and thumb-wheel actions (volume, zoom, horizontal scroll) are injected through the kernel uinput device via ydotool, which needs its background daemon ydotoold running. The installer prefers a packaged ydotool.service / ydotoold.service if your distro ships one; otherwise it writes a minimal ~/.config/systemd/user/ydotoold.service and enables it so it autostarts on login. If ydotoold is missing, shortcut and thumb-wheel actions may not work on Wayland.
The rustup bootstrap¶
The committed Cargo.lock is lockfile format v4 (needs Cargo >= 1.78) and the toml_edit dependency needs rustc >= 1.76. Several distros ship an older toolchain (Ubuntu 24.04 ships Rust 1.75), which cannot build the daemon.
The installer checks the active Cargo version and, if it is older than 1.78 or missing entirely, bootstraps the official toolchain non-interactively:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable --profile minimal
. "$HOME/.cargo/env"
Tip
On Ubuntu 24.04 this happens automatically. If you are installing manually there and cargo build fails on the lockfile version, run the two commands above (or rustup update stable) and rebuild.
Checking a release download¶
Every release lists the SHA-256 of each file in SHA256SUMS, and from 0.4.5 Beta 6 on it carries build provenance: a signed record, made by the release workflow, of the commit and the workflow run that built each file. The installer checks the tarball against SHA256SUMS before it unpacks it. To check a file you downloaded yourself, from the folder it is in:
sha256sum -c SHA256SUMS --ignore-missing
gh attestation verify juhradial-mx-0.4.5-beta.6-linux-x86_64.tar.gz --repo JuhLabs/juhradial-mx
gh attestation verify also takes the release's juhradial-mx-<version>.sigstore.json file with --bundle.
Bazzite, Fedora Atomic and other image-based systems¶
On image-based systems /usr is read-only, so the installer switches to user mode by itself (it checks /run/ostree-booted). You can also ask for it on any distro:
curl -fsSL https://raw.githubusercontent.com/JuhLabs/juhradial-mx/master/install.sh | bash -s -- --user
User mode puts everything under your home folder and uses sudo only for the udev rules, the uinput module and adding you to the input group (on Atomic systems it first copies the input entry from /usr/lib/group into /etc/group, which usermod needs). Add --yes to skip the questions.
| Path | Contents |
|---|---|
~/.local/share/juhradial-mx |
Source tree (used for updates) |
~/.local/bin/juhradiald, juhradial-mx, juhradial-settings |
Daemon and launchers |
~/.local/share/juhradial/ |
Overlay, Qt settings app, Flow module, locales, assets |
~/.local/share/applications/ |
.desktop entries (they name the launchers by full path) |
~/.config/systemd/user/juhradialmx-daemon.service |
systemd user service (ExecStart=%h/.local/bin/juhradiald) |
Runtime packages (PyQt6, Qt SVG and Declarative, GTK 4, libadwaita, python-cryptography, ydotool) are checked with rpm -q; the installer offers to layer the missing ones with rpm-ostree install (a reboot makes them active). The daemon comes prebuilt with a release; without one it is compiled with the host toolchain, or inside a Fedora toolbox through distrobox when the host has no compiler (Bazzite and Aurora ship distrobox).
To uninstall a user-mode install:
systemctl --user disable --now juhradialmx-daemon.service
rm -f ~/.local/bin/juhradiald ~/.local/bin/juhradial-mx ~/.local/bin/juhradial-settings
rm -rf ~/.local/share/juhradial-mx ~/.local/share/juhradial
rm -f ~/.local/share/applications/juhradial-mx.desktop ~/.local/share/applications/org.kde.juhradialmx.settings.desktop
rm -f ~/.config/systemd/user/juhradialmx-daemon.service ~/.config/autostart/juhradial-mx.desktop
sudo rm -f /etc/udev/rules.d/99-juhradialmx.rules /etc/udev/rules.d/70-juhradialmx-uaccess.rules /etc/udev/rules.d/60-ydotool-uinput.rules && sudo udevadm control --reload-rules
Requirements¶
- A Wayland compositor (KDE Plasma 6, GNOME, Hyprland, COSMIC, Sway, niri) or X11.
- Rust toolchain (Cargo >= 1.78) to build the daemon. The installer provides this if needed.
- Python 3 with PyQt6 (overlay and the Qt/QML settings app; the Qt settings app needs Qt 6.4 or newer) and GTK4 + libadwaita via PyGObject (the fallback GTK settings app below that).
- XWayland, used for overlay window positioning on Wayland.
- A supported mouse (Logitech MX Master 4 / 3S / 3 for full HID++, or any mouse in generic evdev mode). See FAQ for device coverage.
Manual installation per distro¶
Each manual path is: install dependencies, clone, build the daemon, then run. To get autostart, udev rules, the input group, and ydotoold set up exactly as the one-line installer does, prefer the installer or replicate the steps in What the installer does.
Fedora¶
Family also covers RHEL, CentOS Stream, Rocky, AlmaLinux, Nobara, Ultramarine.
sudo dnf install -y \
rust cargo \
python3 python3-pip \
python3-pyqt6 qt6-qtsvg qt6-qtdeclarative \
python3-gobject gtk4 libadwaita \
gtk4-layer-shell \
python3-cryptography \
dbus-devel systemd-devel \
libevdev-devel hidapi-devel \
ydotool \
git make
Ubuntu / Debian¶
Family also covers Linux Mint, Pop!_OS, elementary, Kali, Zorin, KDE neon, MX Linux.
sudo apt-get update
sudo apt-get install -y \
rustc cargo \
python3 python3-pip python3-venv \
python3-pyqt6 python3-pyqt6.qtsvg python3-pyqt6.qtqml python3-pyqt6.qtquick \
qml6-module-qtqml qml6-module-qtqml-workerscript \
qml6-module-qtquick qml6-module-qtquick-window \
qml6-module-qtquick-controls qml6-module-qtquick-templates \
qml6-module-qtquick-layouts qml6-module-qtquick-shapes \
qml6-module-qtquick-dialogs qml6-module-qt-labs-folderlistmodel \
python3-gi python3-gi-cairo gir1.2-gtk-4.0 gir1.2-adw-1 \
python3-cryptography \
libdbus-1-dev libsystemd-dev \
libevdev-dev libhidapi-dev \
ydotool \
git make build-essential
# Optional: layer-shell support where the package exists
sudo apt-get install -y libgtk4-layer-shell0
# Newer Qt only (frosted cards from Qt 6.5, vector illustrations from 6.8):
# skip the ones your release does not have, Settings draws stand-ins
sudo apt-get install -y qml6-module-qtquick-effects qml6-module-qtquick-vectorimage qt6-svg-plugins
Debian and Ubuntu package every QML module on its own. With one of them
missing, juhradial-settings names it in the terminal and the page that needs
it shows the command to install it.
Note
On Ubuntu 24.04 the distro Rust (1.75) is too old for the committed lockfile. Bootstrap rustup as shown in The rustup bootstrap before building.
Arch Linux¶
Family also covers Manjaro, EndeavourOS, Garuda, Artix, CachyOS, ArcoLinux, Archcraft.
sudo pacman -S --noconfirm --needed \
rust \
python python-pip \
python-pyqt6 qt6-svg qt6-declarative \
python-gobject gtk4 libadwaita \
gtk4-layer-shell \
python-cryptography \
dbus systemd-libs \
libevdev hidapi \
ydotool \
git make base-devel
openSUSE Tumbleweed¶
sudo zypper install -y \
rust cargo \
python3 python3-pip \
python3-PyQt6 qt6-declarative-imports \
python3-gobject gtk4 libadwaita-devel \
python3-cryptography \
dbus-1-devel systemd-devel \
libevdev-devel libhidapi-devel \
ydotool \
git make
# layer-shell may not be in your enabled repos; safe to skip if unavailable
sudo zypper install -y gtk4-layer-shell || true
Note
On openSUSE the PyQt6 package is python3-PyQt6 (capital Q), unlike the lowercase python3-pyqt6 used on Fedora and Debian. gtk4-layer-shell is not always present in the default repositories; the app still runs without it (layer-shell is only required for niri-style compositors that lack cursor IPC).
NixOS (flake)¶
The repository ships a flake.nix exposing a package, a NixOS module, and a dev shell.
Try it without installing:
Enable it declaratively in your system flake. Add the input:
Then import the module and enable the service in your host config:
The module installs the package system-wide, applies the bundled 99-juhradialmx.rules udev rules for non-root device access, and ensures the input group exists. Rebuild with sudo nixos-rebuild switch --flake .#<host>.
Contributors can drop into a ready toolchain (Rust, Python with PyQt6 + PyGObject, GTK4/libadwaita, Qt6) with:
Build from source¶
If you want to build and run without a full system install:
git clone https://github.com/JuhLabs/juhradial-mx.git
cd juhradial-mx
# Build the Rust daemon
cd daemon && cargo build --release && cd ..
# Run the overlay + daemon
./scripts/juhradial-mx.sh
The daemon binary lands at daemon/target/release/juhradiald. You still need the runtime dependencies for your distro (PyQt6, GTK4/libadwaita, ydotool) and, for non-root device access, the udev rules and input group membership described above. For a full system install identical to the one-line path, run install.sh from the cloned tree.
To run the daemon directly with verbose logging while debugging, see Troubleshooting:
Updating¶
Re-run the same one-line command. When /opt/juhradial-mx already exists, the installer switches to upgrade mode: it downloads the newest release tarball (betas included; it carries a prebuilt daemon, which is used when it runs on your machine), reinstalls files and udev rules, and restarts the running service. When no release asset can be downloaded it falls back to fetching master and building from source.
To install the current master instead of the newest release (for a fix that has been merged but not tagged yet):
curl -fsSL https://raw.githubusercontent.com/JuhLabs/juhradial-mx/master/install.sh | JUHRADIAL_FROM_SOURCE=1 bash
On NixOS, update the flake input and rebuild:
nix flake update juhradial-mx # in your system flake directory
sudo nixos-rebuild switch --flake .#<host>
For a source checkout, pull and rebuild:
After installing¶
- Launch from your application menu (JuhRadial MX) or run
juhradial-mx. Hold the gesture (thumb) button on your MX Master to open the radial menu. - Open the settings app (
juhradial-settings) to remap buttons, pick a theme, and configure haptics, scroll, Easy-Switch, and Flow. See Configuration and Features. - Check the daemon:
Info
Two cases need a session restart (log out and back in): first time your user is added to the input group, and on GNOME for the cursor-helper Shell extension to load. If the menu appears at the top-left corner on GNOME, this is why.
If the menu does not appear or the mouse is not detected, head to Troubleshooting. For how the daemon, overlay, and settings app fit together, see Architecture. Return to Home for the full page index.