I'm becoming less surprised when things Just Work in Linux; I just plugged it in and the proper modules loaded themselves.
My original goal is to control the volume for mpd, but when buying it I imagined I could use it to switch tracks, make other fun toys, etc. But what software do I use?
There's Gizmo, but I couldn't find any docs beyond how to install (too simple) and an API reference (too complicated). Then here are Ruby bindings, which are fun enough, but I found once I hooked it up to mpd that the rotate events come out faster than you can send volume-change events to the daemon.
So as an opportunity to learn some more Haskell, I wrote the glue code myself:
Haskell's FFI is pretty neat: it's sorta like Perl's Inline except it's part of the language definition. With this utility
hsc2hs
, you write normal Haskell but put in some stuff like #include <linux/input.h>
and then anywhere you need constants from the header file you can just use them inline by wrapping them with #{const ...}
.I had initially followed the Ruby code, which had manually encoded the
ioctl
parameter encoding, but once I realized how the FFI worked I instead used the header and invoked macros like #{const EVIOCGNAME(255)}
.Here's a cute function to dump a 32-bit word as binary, though:
showBinary :: Word32 -> String showBinary word = concatMap showBit [31,30..0] where showBit n = if Data.Bits.testBit word n then "1" else "0"
What else is interesting? I'm feeling a bit better about Haskell, because I recognized something: I find that Haskell is so expressive that I tend to get frustrated when I can't find the perfect way to say what I want in just a few lines, when in fact I really just need to "write it out". When I'm programming in C++ I sorta expect to have repeat myself and type a lot so it doesn't bother me as much. So, for example, instead of trying to figure out the perfect combination using a monad transformer, it's not so bad to just have some extra variables lying around.
There are a few walls that I seem to always run into, though, and I'm beginning to wonder if that's just the way things always will be. This code needs to maintain a connection to the mpd server to send volume-change and pause notifications, but you only learn the connection is gone once you try to write to it. So I catch the write exception, and reopen the connection, and retry... except now I need to propagate that new connection to the rest of the code, and of course there's no mutation so I can't just overwrite the old one.
I ended up with a sort of "threaded" main loop, which takes a connection as an argument and produces a connection at the end, but this feels like I'm going about it the wrong way. None of the rest of the code cares if its connection to the server changes (except, of course, when it's in mid-communication). I'm tempted to use a mutable variable...