Handheld Daemon is a project that aims to provide utilities for managing handheld devices. It features a fully functional controller emulator that exposes gyro, paddles, LEDs and QAM across Steam, RPCS3, Dolphin and others. In addition, it features TDP controls all Ryzen devices and bespoke manufacturer controls for the Legion Go and ROG Ally. It brings all supported devices up to parity with Steam Deck. Read supported devices to see if your device is supported.
Handheld Daemon exposes configuration through an API, with a gamemode overlay (double press/hold Side Menu), Decky plugin (hhd-decky), web app (hhd.dev) and desktop app (hhd-ui).
Current Features:
- DualSense and Dualsense Edge emulation
- All buttons supported
- Rumble feedback
- Touchpad support (Steam Input as well)
- LED remapping
- Xbox Elite emulation
- No weird glyphs
- Back button support
- Complete SDL UInput Emulation
- Joycon (Left, Right, Pair), Switch Pro, Dualsense (Edge), Xbox One, Xbox Series X, Xbox 360
- Gyro + Paddles for all SDL apps
- Dual Evdev Motion Controllers for Legion Go
- Virtual Touchpad Emulation
- Fixes left and right clicks within gamemode when using the device touchpad.
- Power Button plugin for Big Picture/Steam Deck Mode
- Short press makes Steam backup saves and wink before suspend.
- Long press opens Steam power menu.
- TDP Controls (adjustor)
- For ROG Ally and Legion Go:
- TDP, Fan Curves, Charge Limiting the Asus and Lenovo way
- Asus: Kernel Driver
- Lenovo: acpi_call while the kernel driver is being developed
- For Other Devices without firmware TDP controls:
- acpi_call + AMD's official manufacturer TDP ACPI bindings
- Ayaneo, Ayn, GPD, OneXPlayer
- For ROG Ally and Legion Go:
- Configuration:
- Fully Featured Gamemode (Gamescope) Overlay
- Desktop App
- Web app
- Config files
- Built-in updater.
The following devices have been verified to work correctly, with TDP, QAM, Paddles/extra buttons, RGB remapping, Touchpad, and Gyro support. The gyro axis might be incorrect for some of those devices, and can be easily fixed in the configuration menu by following these steps. If you do take the time, please open an issue with the correct mapping so it is added to your device.
- Legion Go
- ROG Ally
- GPD Win
- Win 4 (No LEDs)
- Win Mini
- Win Max 2 2023
- Ayaneo
- Air Standard/Plus/Pro
- 1S/1S Limited
- 2/2S
- GEEK, GEEK 1S
- NEXT Lite/Pro/Advance
- SLIDE
- 2021 Standard/Pro/Pro Retro Power
- NEO 2021/Founder
- Ayn
- Loki Zero/Max
- AOKZOE
- A1 Normal/Pro (No LEDs)
- Onexplayer
- Mini Pro
In addition, Handheld Daemon will attempt to work on Ayaneo, Ayn, Onexplayer, and GPD Win devices that have not been verified to work (controller emulation will be off on first start). If everything works and you fix the gyro axis for your device, open an issue so that your device can be added to the supported list. The touchpad will not work for devices not on the supported list.
Use the following script to install Handheld Daemon or find your OS here:
curl -L https://github.com/hhd-dev/hhd/raw/master/install.sh | sh
You can use this script on NobaraOS (after uninstalling the built-in Handheld Daemon/HandyGCCS). ChimeraOS up to 45-1 is not supported due to general instability (uninstall HandyGCCS if you do). This does not work and is not needed on Bazzite, see here.
Important
For non-gaming distros, see here for a partial list of kernel
patches. This includes acpi_call
for TDP on devices other than the Ally.
You can also install the Decky plugin (optional; decky required): (instructions):
curl -L https://github.com/hhd-dev/hhd-decky/raw/main/install.sh | sh
We are sorry to see you go, use the following to uninstall:
curl -L https://github.com/hhd-dev/hhd/raw/master/uninstall.sh | sh
If you find any issues with the latest version of Handheld Daemon you can use any version by specifying it with the command below.
sudo systemctl stop hhd_local@$(whoami)
~/.local/share/hhd/venv/bin/pip install hhd==2.6.0
sudo systemctl start hhd_local@$(whoami)
You can hold the ROG Crate button to switch to the ROG Ally's Mouse mode to turn the right stick into a mouse.
Combinations with the ROG, Armory Crate buttons is not supported in the Ally, you can swap them with start/select for this functionality.
In order for the back buttons in GPD Win Devices to work, you need to map the back buttons to Left: PrintScreen, Right: Pause using Windows. This is the default mapping, so if you never remapped them using Windows you will not have to. Handheld Daemon automatically handles the interval to enable being able to hold the buttons.
Here is how the button settings should look:
Left-key: PrtSc + 0ms + NC + 0ms + NC + 0ms + NC
Right-key: Pausc + 0ms + NC + 0ms + NC + 0ms + NC
Unfortunately, it is not possible to rapid double tap the buttons due to their implementation. The R4 button is mapped to Side Menu (QAM) by default.
You might experience a tiny amount of lag with the Ayaneo LEDs. The paddles of the Ayn Loki Max are not remappable as far as we know.
If you have set any mappings on Legion Space, they will interfere with Handheld Daemon. You can factory reset the Controllers from the Handheld Daemon settings.
The controller gyros of the Legion Go tend to drift and have noise. However, they are excellent after calibration. Calibrate them using steam calibration and be patient, as they will fail a lot. Depending on their state in rare cases they might not be possible to calibrate.
If you are using a kernel older than 6.8, and you are not on a gaming distro (Nobara, Bazzite), you need the following rule for the controllers to be recognized.
# Enable xpad for the Legion Go controllers
ATTRS{idVendor}=="17ef", ATTRS{idProduct}=="6182", RUN+="/sbin/modprobe xpad" RUN+="/bin/sh -c 'echo 17ef 6182 > /sys/bus/usb/drivers/xpad/new_id'"
By default, the Dualsense kernel driver exposes the Dualsense trackpad as a normal trackpad. This means that if you go to use it as steam input, you still get the normal trackpad input. This leads to double input.
You can either disable it with the package ds-inhibit
which detects if steam
is running and mutes it (still works in desktop) or with the following udev rule
(does not work in desktop).
Place it under /etc/udev/rules.d/99-hhd-playstation-touchpad.rules
# Disables all playstation touchpads from use as touchpads.
ACTION=="add|change", KERNEL=="event[0-9]*", ATTRS{name}=="*Wireless Controller Touchpad", ENV{LIBINPUT_IGNORE_DEVICE}="1"
The package ds-inhibit
is available in AUR, packaged for Nobara, and enabled
by default in Bazzite.
If you do not want Playstation glyphs in Steam, you can use
https://github.com/frazse/PS5-to-Xbox-glyphs
as a CSS Loader plugin to switch them to Xbox.
Then, there are CSS plugins for the Legion Go and Ally controller images in
https://github.com/frazse/SBP-Legion-Go-Theme and
https://github.com/semakusut/SBP-ROG-Ally respectively.
If you are using Bazzite, you can also find a ujust
version of the commands
in the Bazzite readme.
Open the overlay (double press side button), or open the desktop app (Handheld Daemon
/$ hhd-ui
),
or go to hhd.dev and enter your device token (~/.config/hhd/token
).
Then just start configuring!
You can also use the Decky plugin (needs Decky):
curl -L https://github.com/hhd-dev/hhd-decky/raw/main/install.sh | sh
The configuration files are stored under ~/.config/hhd
with the main one being
state.yml
, which can be edited and will hot reload.
You can install Handheld Daemon from AUR (Arch) or COPR (Fedora). Both update automatically every time there is a new release.
But, the auto-updater will not work, which is an important feature with devices without a keyboard.
# Arch
yay -S hhd adjustor hhd-ui
# Fedora
sudo dnf copr enable hhd-dev/hhd
sudo dnf install hhd adjustor hhd-ui
sudo systemctl enable hhd@$(whoami)
Handheld Daemon (core; no overlay, TDP) is on nixpkgs
in the unstable
channel.
Add the following to your configuration.nix
to enable:
services.handheld-daemon.enable = true;
services.handheld-daemon.user = "<your-user>";
Handheld Daemon comes pre-installed on Bazzite and updates along-side the system. Most users of Handheld Daemon are on Bazzite and Bazzite releases often happen to bundle Handheld Daemon. Bazzite contains all kernel patches and quirks required for all supported handhelds to work (to the extent they can; certain Ayaneo devices have issues.)
After install, you can use ujust
to install Decky and the Handheld Daemon Decky
plugin with the commands ujust setup-decky
, ujust setup-decky hhd-decky
.
If you need to use a different Handheld Daemon version or a custom one, the
install steps do not currently work for Bazzite, a ujust
command is in the works.
See supported devices to check the status of your device and after install for specific device quirks.
To figure the correct axis from your device, go to steam calibration settings.
Then, in the overlay (double press/hold side button) switch Motion Axis
to
Override
and tweak only the axis (without invert) of your device until they
match the glyphs in steam.
Then, jump in a first person game and turn on Gyro to Mouse
or Camera
.
By default (Yaw
), rotating your device like a steering wheel should turn left
to right,
and rotating it to face down or up should look up or down.
Fix the invert settings of the axis so that it is intuitive.
Finally, switch the setting Gyro Turning Axis
from Yaw
(rotate like a steering
wheel) to Roll
(turn left to right), and fix the remaining axis inversion.
You can now either take a picture of your screen or translate the settings into text (e.g., x is k, y is l inverted, z is j) and open an issue. The override setting also displays the make and model of your device, which are required to add the mappings to Handheld Daemon.
Handheld Daemon fully supports localization through standard PO
, POT
files.
Contribution instructions in progress!!!
You can find pot
and po
files for Handheld Daemon under the i18n
directory.
You can clone/download this repository and open the ./i18n
directory.
Then, just copy the *.pot
files into <your_locale>/LC_MESSAGES/*.po
and begin translating with your favorite text editor, or by using
tool such as Lokalize.
As far as your locale goes, unless you have a good reason to, skip the territory
code (e.g., el
instead of el_GR
).
The files can be updated for a new version with the following commands:
# Prepare dev environment
git clone https://github.com/hhd-dev/hhd
cd hhd
python -m venv venv
pip install babel
pip install -e .
# Regenerate POT files
pybabel extract --no-location -F i18n/babel.cfg -o i18n/hhd.pot src/hhd
# Assuming adjustor is in an adjacent directory
pybabel extract --no-location -F i18n/babel.cfg -o i18n/adjustor.pot ../adjustor/src/adjustor
# Generate PO files for your language if they do not exist
pybabel init -i i18n/hhd.pot -d i18n -D hhd -l YOUR_LANG
pybabel init -i i18n/adjustor.pot -d i18n -D adjustor -l YOUR_LANG
# Update current PO files for your language
pybabel update -i i18n/hhd.pot -d i18n -D hhd -l YOUR_LANG
pybabel update -i i18n/adjustor.pot -d i18n -D adjustor -l YOUR_LANG
Either follow Automatic Install
or Manual Local Install
to install the base rules.
Then, clone, optionally install the userspace rules, and run.
# Clone Handheld Daemon
git clone https://github.com/hhd-dev/hhd
cd hhd
python -m venv venv
source venv/bin/activate
pip install -e .
# Install udev rules to allow running without sudo (optional)
# but great for debugging (not all devices will run properly, the rules need to be expanded)
sudo curl https://raw.githubusercontent.com/hhd-dev/hhd/master/usr/lib/udev/rules.d/83-hhd-user.rules -o /etc/udev/rules.d/83-hhd-user.rules
# Modprobe uhid to avoid rw errors
sudo curl https://raw.githubusercontent.com/hhd-dev/hhd/master/usr/lib/modules-load.d/hhd-user.conf -o /etc/modules-load.d/hhd-user.conf
# You can now run hhd in userspace!
hhd
# Use the following to run with sudo
sudo hhd --user $(whoami)
Handheld Daemon is licensed under THE GNU GPLv3+. See LICENSE for details. A small number of files are dual licensed with MIT, and contain SPDX headers denoting so. Versions prior to and excluding 2.0.0 are licensed using MIT.