A small helper that lets you hold a modifier (for example Left Alt or Super) and use the mouse wheel to change the system volume. While the modifier is pressed the helper suppresses wheel events so foreground apps do not scroll, and it forwards other mouse activity through an injected virtual device so your desktop keeps working as expected.
Volume OSD opened in the top-right purely for the demo; the helper works without keeping the mixer open.
- Hold a keyboard key or extra mouse button and scroll to adjust volume in 5% steps (configurable).
- Supports standard and high-resolution wheels; picks
wpctl/pactl/amixerautomatically. - Suppresses scroll ticks while the modifier is held; optional pointer freeze to avoid acceleration jumps.
- Accepts Alt or Super from the keyboard, BTN_SIDE/BTN_EXTRA, and extra mouse interfaces that emit the modifier.
- Systemd user service for automatic start on login.
- Linux with
/dev/inputaccess and theuinputkernel module. - Python 3.9+ with the
evdevpackage (pip install --user evdev). - Ability to run
sudoduring installation (writes udev/hwdb rules and loadsuinput).
git clone https://github.com/ozdotdotdot/Volume-Wheel-on-Linux.git
cd Volume-Wheel-on-Linux
./install.shThe installer will:
- Let you pick the mouse and keyboard devices from
/dev/input/by-id. - Offer detected auxiliary interfaces (e.g.
-if01-event-kbd) that also emit the modifier when you press a mouse button. - Install
/etc/udev/hwdb.d/90-volume-wheel.hwdband/etc/udev/rules.d/90-volume-wheel.rulesso the virtual device inherits the same identifiers/DPI/wheel click angle as your physical mouse. - Ensure
uinputloads at boot and add your user to theinputgroup (you may need to log out/in once). - Create
~/.config/systemd/user/volume-wheel.serviceand optionally start it immediately.
You can run the helper directly without the service:
python3 Volume-wheel \
--mouse /dev/input/by-id/usb-Logitech_Gaming_Mouse_G502_XXXXXXXX-event-mouse \
--keyboard /dev/input/by-id/usb-Razer_Razer_Huntsman-event-kbd \
--extra-devices /dev/input/by-id/usb-Logitech_Gaming_Mouse_G502_XXXXXXXX-if01-event-kbd \
--volume-step 5% \
--no-forward-pointer # optional: freeze pointer while the modifier is heldThe --extra-devices flag accepts a colon-separated list. Device paths can also be provided through environment variables (VOLUME_WHEEL_MOUSE_PATH, VOLUME_WHEEL_KEYBOARD_PATH, VOLUME_WHEEL_EXTRA_PATHS, VOLUME_WHEEL_STEP, VOLUME_WHEEL_NO_FORWARD_POINTER).
- Modifier source – by default Left/Right Alt, Left/Right Super, and BTN_SIDE/BTN_EXTRA are recognised (including from any devices listed in
MOUSE_ALT_BUTTON_CODES). You can editVolume-wheelto add or remove button codes (seeMOUSE_ALT_BUTTON_CODESandALT_KEY_CODES). - Volume step – set
VOLUME_WHEEL_STEP=2%(or use--volume-step) to change the percentage applied on each wheel tick. - Pointer forwarding – set
VOLUME_WHEEL_NO_FORWARD_POINTER=1(or use--no-forward-pointer) to freeze pointer motion while the modifier is held if your desktop applies a different acceleration curve to the virtual device. - Service location – the systemd unit lives in
~/.config/systemd/user/volume-wheel.service. Disable it withsystemctl --user disable --now volume-wheel.serviceif you want to manage the helper manually.
- The script expects to run with permission to grab the selected mouse device. If it cannot grab, it still changes volume but scroll suppression will not be global.
- When running from source, remember to stop the systemd service first to avoid two instances competing for
/dev/inputaccess.
- Pointer acceleration jump on some desktops – certain environments (notably GNOME on Wayland) may still apply a different acceleration curve when the virtual mouse is active. If you notice the cursor speeding up while the modifier is held, you can work around it by running the helper with pointer forwarding disabled: start the script with
--no-forward-pointer(or setVOLUME_WHEEL_NO_FORWARD_POINTER=1in the systemd unit). This freezes pointer movement while Alt is down but keeps wheel suppression and volume control intact.
Contributions to improve the udev/libinput alignment are welcome.
