Wednesday, September 16, 2015

libratbag - a library for configurable mice

Many modern mice have the ability to store profiles, customize button mappings and actions and switch between several hardware resolutions. A number of those mice are targeted at gamers, but the features are increasingly common in standard mice. Under Linux, support for these device is spotty, though there are a few projects dedicated to supporting parts of the available device range. [1] [2] [3]

Benjamin Tissoires and I started a new project: libratbag. libratbag is a library to provide a generic interface to these mice,enabling desktop environments to provide configuration tools without having to worry about the device model. As of the time of this writing, we have partial support for the Logitech HID++ 1.0 (G500, G5) and HID++ 2.0 protocols (G303), the Etekcity Scroll Alpha and Roccat Kone XTD. Thomas H. P. Anderson already added the G5, G9 and the M705.

git clone http://github.com/libratbag/libratbag

The internal architecture is fairly simple, behind the library's API we have a couple of protocol-specific drivers that access the mouse. The drivers match a specific product/vendor ID combination and load the data from the device, the library then exports it to the caller as a struct ratbag_device. Each device has at least one profile, each profile has a number of buttons and at least one resolution. Where possible, the resolutions can be queried and set, the buttons likewise can be queried and set for different functions. If the hardware supports it, you can map buttons to other buttons, assign macros, or special functions such as DPI/profile switching. The main goal of libratbag is to unify access to the devices so a configuration application doesn't need different libraries per hardware. Especially short-term, we envision using some of the projects listed above through custom backends.

We're at version 0.1 at the moment, so the API is still subject to change. It looks like this:

#include <libratbag.h>

struct ratbag *ratbag;
struct ratbag_device *device;
struct ratbag_profile *p;
struct ratbag_button *b;
struct ratbag_resolution *r;

ratbag = ratbag_create_context(...);
device = ratbag_device_new_from_udev(ratbag, udev_device);

/* retrieve the first profile */
p = ratbag_device_get_profile(device, 0);

/* retrieve the first resolution setting of the profile */
r = ratbag_profile_get_resolution(p, 0);
printf("The first resolution is: %dpi @ %d Hz\n",
       ratbag_resolution_get_dpi(r),
       ratbag_resolution_get_report_rate(r));

ratbag_resolution_unref(r);

/* retrieve the fourth button */
b = ratbag_profile_get_button(p, 4);

if (ratbag_button_get_action_type(b) == RATBAG_BUTTON_ACTION_TYPE_SPECIAL &&
    ratbag_button_get_special(b) == RATBAG_BUTTON_ACTION_SPECIAL_RESOLUTION_UP)
    printf("button 4 selects next resolution");

ratbag_button_unref(b);
ratbag_profile_unref(p);
ratbag_device_unref(device);
ratbag_unref(device);

For testing and playing around with libratbag, we have a tool called ratbag-command that exposes most of the library:

$ ratbag-command info /dev/input/event8
Device 'BTL Gaming Mouse'
Capabilities: res profile btn-key btn-macros
Number of buttons: 11
Profiles supported: 5
  Profile 0 (active)
    Resolutions:
      0: 800x800dpi @ 500Hz
      1: 800x800dpi @ 500Hz (active)
      2: 2400x2400dpi @ 500Hz
      3: 3200x3200dpi @ 500Hz
      4: 4000x4000dpi @ 500Hz
      5: 8000x8000dpi @ 500Hz
    Button: 0 type left is mapped to 'button 1'
    Button: 1 type right is mapped to 'button 2'
    Button: 2 type middle is mapped to 'button 3'
    Button: 3 type extra (forward) is mapped to 'profile up'
    Button: 4 type side (backward) is mapped to 'profile down'
    Button: 5 type resolution cycle up is mapped to 'resolution cycle up'
    Button: 6 type pinkie is mapped to 'macro "": H↓ H↑ E↓ E↑ L↓ L↑ L↓ L↑ O↓ O↑'
    Button: 7 type pinkie2 is mapped to 'macro "foo": F↓ F↑ O↓ O↑ O↓ O↑'
    Button: 8 type wheel up is mapped to 'wheel up'
    Button: 9 type wheel down is mapped to 'wheel down'
    Button: 10 type unknown is mapped to 'none'
  Profile 1
      ...
And to toggle/query the various settings on the device:
$ ratbag-command dpi set 400 /dev/input/event8
$ ratbag-command profile 1 resolution 3 dpi set 800 /dev/input/event8
$ ratbag-command profile 0 button 4 set action special doubleclick

libratbag is in a very early state of development. There are a bunch of FIXMEs in the code, the hardware support is still spotty and we'll appreciate any help we can get, especially with the hardware driver backends. There's a TODO in the repo for some things that we already know needs changing. Feel free to browse the repo on github and drop us some patches.

Eventually we want this to be integrated into the desktop environments, either in the respective control panels or in a standalone application. libratbag already provides SVGs for some devices we support but we'll need some designer input for the actual application. Again, any help you want to provide here will be much appreciated.

15 comments:

Markino said...

Would be interesting have supporto For gaming keybooard too

Markino said...

Would be interesting have supporto For gaming keybooard too

Peter Hutterer said...

We don't have any plans for keyboards at this point in time, the mouse configuration is a much better defined problem scope and something we actually have a chance of completing. Keyboards gets much more complicated quickly, and the use of keyboards with special modes is more restricted to gaming where we can punt the problem of configuration to the games themselves.

Clément Vuchener said...

I think gaming keyboard and gaming mice are close: it is all about rebinding keys and switching profiles with perhaps some LEDs too.

How do you plan to handle the different macro capabilities of different devices? That was my biggest problem when I was thinking about developing such a program. I have worked on both my Corsair K90 keyboard and Logitech G500 and G500s and Logitech's macro are actually much more complex than Corsair's. As you can see in my documentation it as jumps and conditions.

I had a quick look at the code and I don't understand how you are dealing with the different hidpp1.0 mice. They use the same communication protocol but the data is actually very different. If I remember correctly the only common thing between the G5 and G500 is the refresh rate setting. And even the G500 and G500s don't store the resolution in the same way.

Also it looks like you are using CMD_PROFILE for getting the current profile. This command is still weird to me, did you find how to use it properly?

microcolonel said...

I think keyboards have a more to them, and some of them a LOT more.

Then there are devices which aren't really mice or keyboards; I have a foot pedal which can generate a vast array of HID signals (It has both a keyboard and a mouse interface on one device), in a wide array of modes including, but not limited to, single-key momentary, single-key toggle, text sequence entry, mouse move, mouse clicks, double mouse clicks, etc. These are all exclusive configurations as well.

That's just one device.


Anyhow, thanks for starting on libratbag, I look forward to seeing what comes of it. I intend to build something for that foot pedal I'm whining about(because currently I can only configure it reliably through a windows VM, and don't really know what I'm doing if I send what I think are valid packets to it over libusb)

Thomas H.P. Andersen said...

Clément: Funny thing is that the G9 and G9x seems to store the resolution differently too. The G9x works in the way libratbag expects but the G9 seems to give me nonsense values.

Anonymous said...

I started doing something like this a while ago and I already have some code done. I have a G700s and I can probably contribute to this with some of my code. I just need to get my PC back with me ;) Is there anyway to chat with you guys about this project?

Clément Vuchener said...

According to https://blog.cryptomilk.org/2011/02/22/logitech-linux-mouse-support/ the G9 should use something similar to the G5 but with extra resolution modes.

DaVince said...

This library is out for a day and people are already requesting keyboard support... Come on, folks, that'd be a different library altogether. :P

I think this is an excellent idea. Even my simple Dell mouse has two extra buttons on the side that I'd love to be able to configure.

Peter Hutterer said...

Clement: the hw is close for mice and keyboards, but the usage is different. mice are a lot more obvious, especially in the desktop environment. we may be able to support keyboards at some point, but we'll need to figure out what we want to do with them first.
The current hidpp10 implementation is based on your docs, with the occasional difference to our device. Logitech has some protocol data available (mostly for 2.0) and we have access to a little bit more through Red Hat. But you'll see the number of fixmes in the code of what we currently have. Supporting multiple devices is easy though, worst case it's a separate driver each, best case it's just conditions. there isn't a limit on the backends we can add.
I'm not claiming we'll support every device generically, that won't be possible. it'll be a job of adding them one-by-one.

Thomas is already working on moving driver assignment to udev, so that will also allow us to handle sub-driver differences like the G9/G9x.

Wyatt said...

Leave libratbag to mice; keyboards should be in...uh, libpianohands?

Peter Hutterer said...

Carlos: please open an issue on github, that's probably the best for now.

JA said...

Do you have an IRC channel by chance?

Peter Hutterer said...

we do now, #libratbag on freenode. Don't expect too much traffic there though :)

robertc said...

What about Roccat?

http://roccat.sourceforge.net/