Linux Fu: The Cheap Macropad Conundrum

You can get cheap no-brand macropads for almost nothing now. Some of them have just a couple of keys. Others have lots of keys, knobs, and LEDs. You can spring for a name brand, and it’ll be a good bet that it runs QMK. But the cheap ones? Get ready to download Windows-only software from suspicious Google Drive accounts. Will they work with Linux? Maybe.

Of course, if you don’t mind the keypad doing whatever it normally does, that’s fine. These are little more than HID devices with USB or Bluetooth. But what do those keys send by default? You will really want a way to remap them, especially since they may just send normal characters. So now you want to reverse engineer it. That’s a lot of work. Luckily, someone already has, at least for many of the common pads based around the CH57x chips.

Open Source Configuration

Thanks to [Mikhail Trishchenkov], you can use a nice Linux tool to easily configure your macropad. You can build it from source, or get built versions for Linux, Windows, and Mac. The whole thing is written in Rust if you want to take it apart or modify it.

The configuration might not make GUI users happy, but most Linux users are just fine with editing a yaml file. The software works with lots of different pads, so you do have to explain what you have first. Then you can explain what you want.

The yaml file has several keys of interest (documented in the sample file):

  • orientation – You can ask the software to treat the pad in its normal orientation or rotated 90, 180, or 270 degrees. This only matters because it is nice to lay out the keys in the right order and you want the knobs clockwise and counterclockwise directions to make sense. Of course, you can do the mental gymnastics to set it up however you like, but this makes it easier.
  • row, columns – Different pads have different number of rows and columns. Note that this doesn’t respect your Orientation setting. So if you put any knobs to the left, the horizontal keys are the columns and the vertical keys are the rows.
  • knobs – Your pad may have knobs. Count them here.
  • layers – You define multiple layers here (but at least one). The cheaper pads only support one layer, but the nicer ones have a pushbutton and LEDs that let you cycle through a few layers of different key definitions.
  • buttons – Inside a layer, you can have a bunch of key names in brackets. Depending on the orientation, there will be one set of brackets for each column or one set for each row.
  • knobs – Also inside a layer, you can define what happens on ccw, cw, and press events for each button.

The Key

The key names are generally characters (“2” or “d”) but can also be names of keys like “play” or “ctrl-x.”  You can set up multiple keys (“a+b”) and there are mouse events like “click” and “wheeldown.”

You can probably guess most keys, but if in doubt, call the configuration with the show-keys argument to get a list.

I renamed the program to macropad-tool. (ch57x-keyboard-tool was too much to type.)  I didn’t realize at the time that there was another program that should work with the same pad that already uses that name.

When you have a yaml file ready, you can verify it and then, if it went well, upload it:

macropad-tool validate myconfig.yaml
macropad-tool upload myconfig.yaml

That’s It?

That’s mostly it. There were only a few problems. First, you need to reinitialize the macropad each time. Second, you probably need to be root to write to the device, which is less than handy. You probably want to do more than just keystrokes. For example, you want to have the top left button bring up, for example, Gimp. As a stretch goal, my macropad didn’t support layers, and even if it did, it isn’t handy to have to push a little button to change them. I set out to fix that — sort of.

Last Problem First

The KDE keyboard shortcut dialog can read the keys and make them do actions.

At first, I thought it would be easy to map things since I use KDE. I set the keypad up to generate F13-F25, keys you don’t normally have on most keyboards. It worked, but apparently my setup sees these keycodes as other special characters that are already mapped to things. I could have fixed it, but I decided to go a different direction.

The likelihood that you would bind something to Control+Alt+Shift+… is small. Generally, only a few odd and dangerous keystrokes use this because it takes a lot of dexterity to press all those keys.

But the macropad doesn’t care. So I set up the first key to be Control+Alt+Shift+A, followed by Control+Alt+Shift+B, and so on. Now, I can easily use the KDE keyboard shortcuts from the control panel to catch those keys and do things like launch a shell, change desktops, or whatever.

All the Rest

All the other problems hinge on one thing: it is hard to run the command to initialize the macropad unless you are root. If you had a simple command to set it up, you could easily run it on startup or at any time you wanted to reinitalize the macropad.

In addition, you could bind a key to run the configuration tool to change configurations to make a poor version of layers. Sure, there would be no indication of what layer you were in, but you could fix that a different way (for example, status text on the taskbar). While not ideal, it would be workable.

So how do we get a simple command that can easily load the macropad? There are a few choices. You could have a script owned by root that is sticky. That way, users could run it, but it could become root to configure the keyboard.

I decided to go a slightly different way. I put the tool in /usr/local/bin and the yaml files in /usr/local/share/macropad, which I created. Then I created a script. You’ll probably want to modify it.

The script calls the keypad loader with sudo. But the sudo will just prompt you, right? Well, yes. So you could make an entry in /etc/sudoers.d/99-macropad:

 alw ALL=(ALL) NOPASSWD: /usr/local/bin/macropad-tool

Now you, or the script on your behalf, can run the tool with sudo and not provide a password. Since a normal user can’t change /usr/local/bin/macropad or /usr/local/bin/macropad-tool, this is reasonable.

If you prefer, you could write a udev rule to match the USB IDs of your macropad and set the permissions. Something like this:

ATTRS{idProduct}=="8840", ATTRS{idVendor}=="1189", MODE="666", GROUP="users"

If you change the permissions, change the script to not use sudo. And, of course, change the product and vendor IDs to suit your macropad, along with your group, if you need something different. However, that’s probably the best option.

Fake Layers

Since the script allows you to define different layers, you can make a switch change the layer configuration by simply running the script with a given argument on a key press. A hack, but it works. Obviously, each layer will need its own fake keys unless they provide the same function. The file /tmp/macropad-current-layer tracks the current layer, which you can show with something like a command output plasmoid.

Arrange for the script to load on startup using your choice of /etc/rc.local, systemd, or even a udev rule. Whatever you like, and that takes care of all your problems.

Did we mention how cheap these are? Good thing, because you can easily roll your own and put some good software on it like QMK.

6 thoughts on “Linux Fu: The Cheap Macropad Conundrum

  1. Udev rules are always the better choice. Giving an executable (which could have anything in it) full access to your system just so that it can modify a single device is a very bad habit to get into.

  2. Well, I did say that’s the right way to do it. However, there’s really no difference in the other methods from doing sudo xxxx, it just automates it. And if I didn’t mention them, the first comment would have been, “Well, you know you could have used…” so just trying to cover the bases. In this case, the executable is built from source. Is simple enough to read through, doesn’t automatically update, and is a directory a normal user can’t write to, so I think any of the methods are “ok” but I agree, udev for the win.

    1. At least in THEORY, it’s more different than you might think. If you use SELinux, run some /usr/sbin/ command under sudo and it likely runs unconstrained. But storing it in /usr/lib/udev/ and having the system execute it based on device-matching rules means you can encumber it with as much restrictive policy as you please, preventing it from being able to pull any funny business outside of its narrowly-defined lane.

      Granted, in practice almost nothing actually takes advantage of that capability. Literally every executable in /usr/lib/udev on my Fedora 42 box is system_u:object_r:bin_t except for udev-configure-printer (system_u:object_r:cupsd_config_exec_t).

      1. Yes, well on Ubuntu its AppArmor but like you say, I’ve only seen one or two very specialized systems where anyone has done anything meaningful with either of those outside of the distro and even then.

Leave a Reply

Please be kind and respectful to help make the comments section excellent. (Comment Policy)

This site uses Akismet to reduce spam. Learn how your comment data is processed.