Skip to content

Repository files navigation

UInput Macropad

License: GPL v3

UInput Macropad is a lightweight, background daemon that intercepts events from a specific /dev/input/event device and remaps them into macros, custom key combos, or system commands. Since it operates via uinput at the Linux kernel level, it works universally across X11, Wayland, and the TTY console.

The program is based on sebastiansam55/uinput-keyboard-mapper and is designed to gracefully handle device disconnections and reconnections automatically.


Features

  • Universal Compatibility: Works under X11, Wayland, and virtual console/TTY.
  • Multilayer Support: Define multiple layers of key mappings and swap between them on the fly.
  • Layer Transition Commands: Trigger external shell commands automatically when swapping layers (e.g., triggering a desktop notification).
  • Comprehensive Macro Types:
    • cmd: Execute system commands or scripts.
    • key: Remap single keys.
    • keylist: Send sequences of keys in rapid succession.
    • keycomb: Construct complex key down/up sequences with fine-grained control and delay timers.
    • button: Custom handler for relative scroll wheels/axes (e.g., Logitech MX Master 3 thumb wheels).
    • dispose: Intercept and completely discard specific keystrokes.
  • Input Grabbing: Fully absorb original device inputs (using EVIOCGRAB) so only remapped outputs reach the system.
  • Robust Connection Handling: Daemon keeps running and automatically re-grabs your device if it is unplugged and plugged back in.

Requirements

  • Python: Version 3.9 or higher
  • Dependencies: python-evdev library

Installation

  1. Install the required evdev Python package:
    pip3 install evdev
  2. Copy macropad.py to a location in your PATH (e.g., /usr/local/bin/ or ~/.local/bin/) and mark it as executable:
    chmod +x macropad.py

Permissions Setup

To run the daemon as a non-root user (which is highly recommended for security), you need access to the input and uinput subsystems.

Step 1: Input Group Access

Add your user to the input group:

sudo usermod -a -G input $USER

Step 2: Uinput Group Access & Udev Rules

If the virtual keyboard creation still fails, create a dedicated uinput group and udev rules:

  1. Create the uinput group:
    sudo groupadd -f uinput
  2. Add your user to the uinput group:
    sudo gpasswd -a $USER uinput
  3. Create a new udev rule file at /etc/udev/rules.d/99-uinput.rules containing:
    KERNEL=="uinput", GROUP="uinput", MODE="0660"
    
  4. Reboot your system to apply permissions.

Warning

While you can run this script using sudo, any command-based macros (cmd) will run as root, presenting a severe security risk. Always configure non-root permissions.


Usage

Run the program from the terminal or daemon launcher:

macropad.py [options]

Command Line Options

  • -c, --config-file <path>: Path to a custom config file (default: ~/.config/uinput-macropad/config.json).
  • -v, --verbose: Enable verbose logging for debugging and setup (default: False).
  • --full-grab / --no-full-grab: Fully absorb all keystrokes from the grabbed device (default: True).
  • --only-defined / --no-only-defined: If true, key events not mapped to a macro are discarded rather than passed through (default: False).
  • --clone / --no-clone: If true, the virtual output keyboard copies the exact capabilities of the grabbed device (default: True).

Configuration File Format

The config file uses JSON format. Below are structured templates and options:

Basic Layout (No Layer Swap Commands)

See config.json for reference:

{
    "macros": {
        "layer_name_1": [
            ["macro_name", [hotkey_code_list], "macro_type", macro_info_payload],
            ["copy_shortcut", [275], "keycomb", [29, 46, -29, -46]]
        ],
        "layer_name_2": [
            ["paste_shortcut", [276], "keycomb", [29, 47, -29, -47]]
        ]
    },
    "layers": [
        ["layer_name_1", [layer_trigger_keys]],
        ["layer_name_2", [layer_trigger_keys]]
    ],
    "dev_name": "Device Name or Event Path",
    "only_defined": false,
    "full_grab": true,
    "clone": true
}

Dynamic Layer Switching Commands

You can specify a command to run whenever a layer becomes active. This is useful for displaying notifications, changing indicator LEDs, or updating status bars. See layers-commands.json for reference:

"layers": [
    ["test1", [59], ["zenity", "--info", "--title=Layer swap", "--text=Layer 'test1'", "--timeout=2"]],
    ["test2", [60], ["zenity", "--info", "--title=Layer swap", "--text=Layer 'test2'", "--timeout=2"]]
]

Macro Types & Definitions

The daemon supports six macro engines:

1. cmd

Executes external shell commands or scripts using a list formatting similar to Python's subprocess.Popen.

  • Example: ["open_browser", [2], "cmd", ["xdg-open", "https://google.com"]]

2. key

Sends a single virtual keycode.

  • Example: ["map_to_one", [3], "key", [2]] (sends keycode 2, which corresponds to the "1" key)

3. keylist

Sends a sequence of keys in rapid succession (note: this is sequential typing, not a simultaneous combination).

  • Example: ["type_hello", [4], "keylist", [35, 18, 38, 38, 24]]

4. keycomb

Enables custom macro recipes using a sequence of commands matching these data types:

  • Integer (int): Keydown events are positive integers, keyup events are negative integers. (e.g. 29 presses Left Ctrl, -29 releases it).
  • Float (float): Pause execution for the specified duration in seconds (e.g., 0.25 sleeps for 250ms).
  • List (list): Typings sent inside the combo.
  • Example (Ctrl + A): ["select_all", [5], "keycomb", [29, 30, -29, -30]]
  • Example (Capitalized letters "AS!"): ["type_as", [6], "keycomb", [42, 30, -30, 31, -31, 2, -2, -42]]

5. button

Tailored for scroll wheels and analog inputs. Maps relative movements (like -1 or 1) to corresponding keyboard outputs.

  • Example: ["hscroll", [6], "button", {"1": 105, "-1": 106}]

6. dispose

Absorbs and silences the specified keys. Useful when combining mappings so that the host OS doesn't receive standard key events.

  • Example: ["disable_wheel", [12], "dispose", []]

Finding Keycodes & Device Names

To find the names of your devices and keycode integers:

  1. Run evtest in your terminal.
  2. Select your device from the listed options to monitor real-time events.
  3. Press keys on your keyboard/macropad. evtest will print statements containing values like (KEY_LEFTCTRL), code 29.
  4. Use the printed integer code values in your configuration.
  5. Refer to input-event-codes.h for a complete system header list of Linux keycode definitions.

Real World Usage Example

LiveReload + Browser Refresh Macro

This macro automates triggering a LiveReload browser sync in Sublime Text, saving keystrokes and context switches:

Step Details Combo Array Element
1 Open command palette (Ctrl + Shift + P) 29, 42, 25, -29, -42, -25, 0.25,
2 Type "livereload" [38, 23, 47, 18, 19, 18, 38, 24, 30, 32], 0.25,
3 Move cursor down 5 times [108, 108, 108, 108, 108], 0.25,
4 Press Enter [28], 0.25,
5 Move cursor down 4 times [108, 108, 108, 108], 0.25,
6 Press Enter [28], 0.25,
7 Reopen command palette 29, 42, 25, -29, -42, -25, 0.25,
8 Type "browser" [25, 19, 18, 47, 23, 18, 17], 0.25,
9 Press Enter [28], 0.25,
10 Press Enter [28], 0.25

Logitech MX Master 3 Thumbwheel Mappings

A pre-packaged profile is available in logimxmaster3.json. It provides the following remaps:

  • Copy: Maps the mouse "Back" button to send Ctrl + C.
  • Paste: Maps the mouse "Forward" button to send Ctrl + V.
  • Thumb Scroll (HScroll): Translates horizontal side wheel scrolls to left/right arrow key events, allowing horizontal scrolling to work out of the box in VirtualBox, RDP clients, and unsupported Linux applications.

Troubleshooting & Alternatives

Too Complicated?

If editing JSON configuration files is too complex, consider trying:

Note: AutoKey relies on X11 and does not work natively under Wayland or TTY consoles. Because uinput-macropad runs at the uinput kernel subsystem level, it works universally in all environments.


Development & Roadmap

For planned features, refactoring tasks, and upcoming bug fixes, please consult TODO.md. Suggestions and Pull Requests are welcome!

About

UInput linux macropad

Resources

Stars

4 stars

Watchers

2 watching

Forks

Releases

Packages

Contributors

Languages