Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
name: CI

# Build and test on every push / PR. `release.yml` only ever fires on a tag, so
# without this nothing compiles the Linux target until release time — and the
# Linux-gated code (audio/pulse.rs, the non-Windows half of keys.rs,
# autostart.rs) is invisible to a Windows developer's compiler entirely.
on:
push:
branches-ignore:
- "gh-pages"
pull_request:
workflow_dispatch:

concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

env:
CARGO_TERM_COLOR: always

jobs:
linux:
name: Linux (build + test)
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4

- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
pkg-config libudev-dev libxkbcommon-dev libxkbcommon-x11-dev \
libwayland-dev libxcb1-dev libfontconfig1-dev

- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
components: clippy

- name: Cache cargo
uses: Swatinem/rust-cache@v2
with:
key: ci-linux
cache-all-crates: true

# Type-checks the Linux-only modules that never see a compiler on a
# Windows dev machine.
- name: cargo check
run: cargo check --all-targets

- name: cargo test
run: cargo test

- name: cargo build --release
run: cargo build --release

# The shipped Linux binary is a bare dynamically-linked ELF, so anything
# in DT_NEEDED must exist on a plain desktop. Print it so a newly
# introduced native dependency (libxdo, libpulse, ...) is visible in the
# log instead of surfacing as "it won't start" on a user's machine.
- name: Report runtime library dependencies
run: |
ldd target/release/slidr || true
echo "--- DT_NEEDED ---"
readelf -d target/release/slidr | grep NEEDED || true
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ jobs:
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
pkg-config libpulse-dev libudev-dev libxkbcommon-dev libxkbcommon-x11-dev \
libwayland-dev libxcb1-dev libfontconfig1-dev libxdo-dev
pkg-config libudev-dev libxkbcommon-dev libxkbcommon-x11-dev \
libwayland-dev libxcb1-dev libfontconfig1-dev

- name: Install dependencies (Windows build)
if: matrix.target == 'windows'
Expand Down
44 changes: 2 additions & 42 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 8 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,19 +49,21 @@ slint = { version = "1.16", default-features = false, features = ["std", "compat
# Build timestamp injected at compile time
chrono = { version = "0.4", default-features = false, features = ["clock"] }

# Key simulation (cross-platform)
enigo = "0.2"
# Key simulation (cross-platform).
# Linux: the `x11rb` backend (pure Rust XTEST) instead of the default `xdo`
# one. `xdo` is `#[link(name = "xdo")]`, which makes libxdo.so.3 a hard
# load-time dependency of the shipped binary - it would refuse to start on any
# machine without xdotool installed. `x11rb` has the same reach (X11 +
# XWayland) with no native library. Native Wayland clients are unreachable
# either way; `keys.rs` warns about that at startup.
enigo = { version = "0.2", default-features = false, features = ["x11rb"] }

# Cross-platform "open file/url"
open = "5"

# Native file dialog for "Browse…" buttons
rfd = "0.15"

[target.'cfg(target_os = "linux")'.dependencies]
libpulse-binding = "2.28"
udev = "0.8"

[target.'cfg(target_os = "windows")'.dependencies]
# Direct dep so the `#[interface]` macro's `::windows_core` paths resolve; pinned
# to the same version `windows` re-exports as `windows::core` (shared types).
Expand Down
33 changes: 31 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,42 @@ installer/slidr.nsi Interactive NSIS installer

```sh
# Linux dev deps (Ubuntu 24.04 names):
sudo apt install build-essential pkg-config libpulse-dev libudev-dev \
sudo apt install build-essential pkg-config libudev-dev \
libxkbcommon-dev libxkbcommon-x11-dev libwayland-dev libxcb1-dev \
libxdo-dev libfontconfig1-dev
libfontconfig1-dev

cargo run --release
```

### Linux runtime requirements

* **`pactl`** (Ubuntu/Debian/Fedora: `pulseaudio-utils`). The audio backend
shells out to it; it also ships with `pipewire-pulse`. Without it Slidr
starts but every volume/mute action is a no-op.
* **Serial port access.** Opening `/dev/ttyUSB*` / `/dev/ttyACM*` needs
permission, or the connection banner shows "Access denied". Either install
the bundled udev rule (preferred - applies on replug, no group, no re-login):

```sh
sudo install -m644 packaging/99-slidr.rules /etc/udev/rules.d/99-slidr.rules
sudo udevadm control --reload-rules && sudo udevadm trigger
```

or add yourself to the port's group and log out and back in:

```sh
sudo usermod -aG dialout "$USER"
```
* **Key actions need an X11 session.** Simulated keypresses go out over XTEST,
which reaches X11 and XWayland windows but *not* native Wayland windows - a
Wayland compositor does not let an ordinary application inject input. On a
Wayland session Slidr logs a warning at startup and key actions will appear
to do nothing in Wayland-native apps; volume, mute, LED and API actions are
unaffected. Pick the "Xorg"/"X11" session at login if you need key actions.
* **No system tray.** The tray (`src/tray.rs`) is Windows-only, so the
tray-related settings are hidden on Linux and `--hidden` is ignored - it
would otherwise leave a window-less process that cannot be reopened.

Override the config location with `SLIDR_CONFIG_DIR=/path` (default
`$XDG_CONFIG_HOME/slidr`, i.e. `~/.config/slidr`; `%APPDATA%/slidr` on Windows).

Expand Down
3 changes: 2 additions & 1 deletion deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ fi
# ── Publish ──────────────────────────────────────────────────────────────
log "Publishing to $WEBROOT"
mkdir -p "$WEBROOT"
install -m644 target/release/slidr "$WEBROOT/Slidr-linux-x86_64"
# 0755: this is a binary users download and run, not a document.
install -m755 target/release/slidr "$WEBROOT/Slidr-linux-x86_64"
if [[ "$SKIP_WINDOWS" -eq 0 ]]; then
install -m644 installer/Slidr-windows-setup.exe "$WEBROOT/Slidr-windows-setup.exe"
install -m644 "target/$WIN_TARGET/release/slidr.exe" "$WEBROOT/Slidr-windows-portable.exe"
Expand Down
28 changes: 28 additions & 0 deletions packaging/99-slidr.rules
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# udev rules for Slidr — grant the logged-in desktop user access to the board's
# serial port without adding them to a group or running the app as root.
#
# Install:
# sudo install -m644 packaging/99-slidr.rules /etc/udev/rules.d/99-slidr.rules
# sudo udevadm control --reload-rules && sudo udevadm trigger
#
# Without this (or membership in the port's owning group, usually `dialout`),
# opening /dev/ttyUSB* / /dev/ttyACM* fails with "Permission denied (os error 13)"
# and Slidr reports "Access denied" on the connection banner.
#
# The VID list mirrors KNOWN_USB_VIDS in src/serial.rs — keep the two in sync.

# Arduino LLC (Uno, Mega, Nano 33, ...)
SUBSYSTEM=="tty", ATTRS{idVendor}=="2341", TAG+="uaccess"
# Atmel / Arduino Leonardo, Micro, Pro Micro
SUBSYSTEM=="tty", ATTRS{idVendor}=="0d28", TAG+="uaccess"
# WCH CH340 / CH341 (most Nano clones)
SUBSYSTEM=="tty", ATTRS{idVendor}=="1a86", TAG+="uaccess"
# FTDI FT232
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", TAG+="uaccess"
# Silicon Labs CP210x
SUBSYSTEM=="tty", ATTRS{idVendor}=="10c4", TAG+="uaccess"

# `uaccess` hands the device to whoever is logged in at the seat, which is what
# you want on a single-user desktop. On systems without systemd-logind, replace
# the TAG with a group instead, e.g.:
# SUBSYSTEM=="tty", ATTRS{idVendor}=="2341", MODE="0660", GROUP="dialout"
3 changes: 3 additions & 0 deletions src/audio/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

#[cfg(target_os = "linux")]
pub mod pulse;
// Pure `pactl` output parsers. Compiled everywhere (not gated) so they stay
// unit-testable on any host; only `pulse` consumes them.
pub mod pulse_parse;
#[cfg(target_os = "windows")]
pub mod wasapi;
pub mod null;
Expand Down
Loading
Loading