5.1 KiB
surface-dial-linux
A Linux userspace controller for the Microsoft Surface Dial. Requires Linux Kernel 4.19 or higher.
- Uses the
evdevAPI +libevdevto read events from the surface dial. - Uses
libevdevto fake input via/dev/uinput(for keypresses / media controls) - Uses
hidapito configure dial sensitivity + haptics
DISCLAIMER: This is WIP software!
Things will change. Things will break. Things are probably buggy.
There's also a non-zero chance that I'll just stop working on it at some point once I decide that it's Good Enough™️ for me.
You've been warned 👀
Overview
surface-dial-daemon is a background daemon which recieves raw events and translates them to various actions.
Aside from haptic feedback, the daemon also uses FreeDesktop notifications to provide visual feedback when performing various actions.
It would be cool to create some sort of GUI overlay (similar to the Windows one), though that's out of scope at the moment.
Functionality
- Interpret raw Surface Dial event
- Operating Modes
- Volume Controls
- Media Controls
- D-Pad (emulated left, right, and space key)
- Scrolling / Zooming
- (meta) custom modes specified via config file(s)
- Dynamically switch between operating modes
- Using some-sort of on-device mechanism (e.g: long-press)
- Context-sensitive (based on currently open application)
- Mode Persistence (keep mode when dial disconnects)
- Haptic Feedback
- https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/radial-controller-protocol-implementation
- https://www.usb.org/sites/default/files/hutrr63b_-_haptics_page_redline_0.pdf
- https://www.usb.org/sites/default/files/hut1_21.pdf
- This was tricky to figure out, but in the end, it was surprisingly straightforward! Big thanks to Geo for pointing me in the right direction!
- Desktop Notifications
- On Launch
- When switching between modes
- When switching between sub-modes (e.g: scroll/zoom)
Feel free to contribute new features!
Dependencies
Building surface-dial-daemon requires the following:
- Linux Kernel 4.19 or higher
- A fairly recent version of the Rust compiler
libevdevhidapi
You can install Rust through rustup.
Unless you're a cool hackerman, the easiest way to get libevdev and hidapi is via your distro's package manager.
# e.g: on ubuntu
sudo apt install libevdev-dev libhidapi-dev
Building
surface-dial-daemon uses the bog-standard cargo build flow.
cargo build -p surface-dial-daemon --release
The resulting binary is output to target/release/surface-dial-daemon
Running surface-dial-daemon
For testing changes locally, you'll typically want to run the following:
cargo build -p surface-dial-daemon && sudo target/debug/surface-dial-daemon
Note the use of sudo, as surface-dial-daemon requires permission to access files under /dev/input/ and /dev/uinput.
Installation
As you might have noticed, the daemon dies whenever the Surface Dial disconnects (which happens after a brief period of inactivity).
I personally haven't figured out a good way to have the daemon gracefully handle the dial connecting/disconnecting (PRs appreciated!), so instead, I've come up with a cunning plan to spawn the daemon whenever the Surface Dial connects 😉
This will only work on systems with systemd.
If your distro doesn't use systemd, you'll have to come up with something yourself I'm afraid...
# Install the `surface-dial-daemon` (i.e: build it, and place it under ~/.cargo/bin/surface-dial-daemon)
# You could also just copy the executable from /target/release/surface-dial-daemon to wherever you like.
cargo install --path .
# IMPORTANT: modify the .service file to reflect where you placed the `service-dial-daemon` executable
vi ./install/surface-dial.service
# create new group for uinput
# (the 99-uinput.rules file changes the group of /dev/uinput to this new group)
sudo groupdadd -f uinput
# add self to the new uinput group and the existing /dev/input group
sudo gpasswd -a $(whoami) uinput
sudo gpasswd -a $(whoami) $(stat -c "%G" /dev/input/event0)
# install the systemd user service
mkdir -p ~/.config/systemd/user/
cp ./install/surface-dial.service ~/.config/systemd/user/surface-dial.service
# install the udev rules
sudo cp ./install/99-uinput.rules /etc/udev/rules.d/99-uinput.rules
sudo cp ./install/50-surface-dial.rules /etc/udev/rules.d/50-surface-dial.rules
# reload systemd + udev
systemctl --user daemon-reload
sudo udevadm control --reload
You may need to reboot to have the various groups / udev rules propagate.
License
At the moment, this software is deliberately unlicensed. I'm not opposed to adding a license at some point, it's moreso that I don't think the project is at the stage where it needs a license.
