Friday, December 13, 2013

The T440 has a rather unusual touchpad with the buttons painted on top of the touchpad rather than the bottom. In addition, the separate set of buttons for the trackstick have gone the way of the dodo. Moving the software-emulated buttons up on the touchpad is obviously quite important for trackstick users but it throws up a bunch of problems. There are some limitations with the current synaptics X.Org driver: we can only have one region each designated for the right and the middle button. The rest of the touchpad is a left button click. In the case of the T440, the default Windows config has a right button up the top and another one at the bottom of the touchpad. An ASCII-art of that would look like this:

Drop that into /etc/X11/xorg.conf.d/99-t440-synaptics.conf and you're good to go.

The problem is finding a generic solution to this that we can ship in a distribution. That requires a two-step progress. The touchpads look the same as all others, the only differentiator we have is the DMI information on the box. We can't check that in the xorg.conf snippets yet (Daniel Martin is working on a MatchDMI tag, but it won't happen until server 1.16). For now, we need a udev rule to help the xserver.

I've pushed this configuration into Fedora now (rawhide, F20, F19), let's see what the feedback is. Having the whole right-side of the touchpad work as right button may cause a few issues so this is one change I may have to revert in the future.

Thursday, December 12, 2013

If you don't have one of the Wacom serial devices, you probably don't need to worry about the below. If you do, read on.

Wacom serial devices are supported by the xf86-input-wacom driver directly, but a much nicer way is to have the kernel deal with it and thus the device exposed as a normal evdev device to userspace. This way, the device is also picked up by libwacom and, in turn, by the GNOME Wacom panel for configuration and calibration. For the device to work as evdev device you need inputattach (in the linuxconsoletools package on Fedora).

The simplest approach if your device is connected to /dev/ttyS0 is:

$> inputattach --daemon --w8001 /dev/ttyS0

but of course we want to automate this. We used to have a udev rule that fires up inputattach but recent changes [1] in udev broke that ability: udev is now decidedly for short-running processes only, long-running processes started by a udev rule will be killed. The solution to that is a udev rule paired with a systemd service.

First, the systemd wacom-inputtattach@.service service file, which is quite simple:

Note the @ in the name, we use what comes after through the %I directive as the device file, e.g. ttyS0. Note also that we don't use the --daemon flag because we want systemd to take care of our process instead of forking it and letting it guess what the PID is, etc. This service can now be started with

$> systemctl start wacom-inputattach@ttyS0.service

and of course the information is available in the journal, etc.

Now we just need something to start the service automatically, and that's handled in a simple udev rule. Here's the whole udev file for easy copy/paste:

Wacom's serial devices have IDs starting with WACf, so we match on that. The first two "SUBSYSTEM..." lines match and set the name and some ID_foo tags so that the server recognises the tablet as input device and can match against xorg.conf.d InputClass snippets. With just those first two lines, we will have the xf86-input-wacom driver work for serial devices, even if inputattach didn't start. These two lines haven't changed in years either. The new and systemd-specific bit is the third line. Again we match but this time we set the systemd tag, tell systemd the service we want to start (%k is replaced with the kernel device, e.g. ttyS0) and we're done. Reboot [2] and you should be the happy viewer of something like this:

[1] I say recent but really, it could've been any time since F18 or so, I don't test this particular device very often
[2] yeah yeah, there's a command to trigger this directly, you don't need to reboot, I know

Wednesday, December 4, 2013

I guess about 80% of the time coding is spent handling error messages and dealing with the situation where the input isn't what it should be. That's ok for a real project, but sometimes it's just easier to say "this shouldn't happen but make sure I see an error if it does". For example when prototyping, it's enough to know that something went wrong so we can throw out the output. Or get notified when a value that should be within a range is outside that range, etc.
The traditional way of that is assert(3), which checks an expression and aborts the program when the condition is not met. That is a heavy hammer, and not flexible enough in many cases.

So that got me thinking: essentially I wanted a set of macros that I can throw into any function to alert me when something goes wrong. Similar to the BUG_ON in the kernel, or the BUG_WARN in X. But a bit more powerful than that, because the main issue I have with BUG_WARN is that it tells me when a condition fails, but not necessarily why, or what the condition was supposed to check. Two revisions later, Rusty Russell merged my new argcheck CCAN module. It provides a set of macros for error checking that do not abort and, more importantly, also work as conditions. The simplest use-case is:

If percent is outside the [0..100] range, a message with the file, function and line number is printed. And, possibly more useful, it will also print the actual value of percent. It gets more interesting when we use symbolic names, and we use the whole things as a condition:

So not only does it give us the actual values, it also provides the symbolic names (if there are any). Which, for debugging purposes, makes the whole thing quite useful. And we don't need to double-evaluate, we can use the macro as condition. argcheck takes care to only evaluates its arguments once, so there shouldn't be any side-effects. This also goes for disabling argcheck. If ARGCHECK_DISABLE_LOGGING is defined when including argcheck.h no messages are logged but the conditions still exist and the code runs as before. The same goes for a user-defined argcheck_log function (in case you need to override the default fprintf(stderr)).

I've added quite a few macros for various use-cases, including:

argcheck_flag_set(a, flag) - false if the flag isn't set, but also checks that the flag is not 0

argcheck_str_not_zero_len(str) - false if the string is NULL or the empty string

argcheck_str_max_len(str, len) - false if the string is NULL, or longer than len. Works great to warn about truncated string copies.

argcheck_ptr_null(ptr) - false if the pointer is not NULL. Works great to spot uninitialized variables.

Any comments or feedback let me know, I'm eager to improve this to make it even more useful (it already saved my hide a few times)