I decided to move away from Gnome on my netbook desktop, as recent Ubuntu updates caused more problems than they solved, and now I’m back to running my beloved Fluxbox. Of course, doing so means that I lose some conveniences in the Gnome desktop.
For example, when I close my laptop lid, the netbook no longer suspends. I’ve assigned a hotkey to this but maybe I want it to be automatic. This got me thinking about how to make this work. Well, it’s done through DBUS of course.
DBUS has become ubiquitous on Linux systems, providing a sub/pub model for events in Linux, which is a good thing given that the POSIX spec provides very little to make this kind of thing happen. I’m more of a fan of files, directories and symlinks myself, but I can understand why some programmers would want something a little more sophisticated, even if I don’t.
Being a Python hacker I want to use the python dbus module, so I install python-dbus and I’m off and running, albiet with little to no documentation. Google allows me to find enough clues that I’m led to qdbus to query the system bus, and I find org.freedesktop.DeviceKit.Power, which exposes a LidIsClosed property and a Changed event.
So, looking at some examples I manage to get this far…
#!/usr/bin/python
import dbus, gobject
from dbus.mainloop.glib import DBusGMainLoop
power_iface = None
def main():
global power_iface
DBusGMainLoop(set_as_default=True)
bus = dbus.SystemBus()
proxy = bus.get_object('org.freedesktop.DeviceKit.Power',
'/org/freedesktop/DeviceKit/Power')
if __name__ == '__main__':
main()
As the object that I want is not available in my process, I must instantiate a proxy to it via dbus. To get this proxy I call get_object on the bus object, passing the name of the object and then, somewhat redundantly, a path that seems to represent a namespace of properties, signals and methods.
Ok, so now that I have the object I can just check the LidIsClosed property and register for that signal, right? Wrong. For no apparently reason now that I have the object, to call methods on it I need an interface object, representing one of the interfaces that this object implements. That’s just so…Java, that it rubs me the wrong way, but I forge on.
I want the interface that allows me to query properties, and apparently all objects in DBUS implement some common interfaces, because this is how I get the interface I need:
power_iface = dbus.Interface(proxy, 'org.freedesktop.DBus.Properties')
My take on this is that I am asking for the standard freedesktop DBus Properties interface on the DeviceKit.Power object, so that I can query its properties. Overly complex when simple would do? Absolutely. Does it work? Sure, but why would anyone choose to work this hard deliberately? Oh, Java programmers. I forgot.
Now I want to register a listener for any changes from the Power object, which includes the lid being closed. You do this like so, via a callback:
bus.add_signal_receiver(handle_lidclose,
dbus_interface="org.freedesktop.DeviceKit.Power",
signal_name="Changed")
Why you don’t use the existing proxy object to DeviceKit.Power is beyond me. Maybe you can and I simply don’t understand the API. I would have envisioned something like…
proxy.Changed.listen(handle_lidclose)
…but that’s me. I guess some people really like typing.
Now, we finish this off by writing our handler…
def handle_lidclose(*args):
closed = power_iface.Get('org.freedesktop.DBus.Properties', 'LidIsClosed')
if closed:
print "lid is closed"
else:
print "lid is open"
…and starting our main loop via…
loop = gobject.MainLoop()
loop.run()
All that’s left is to write the call in my handler to suspend the laptop if
the lid is closed, but honestly, my fingers are tired. I particularly love the
way that I need to pass the full interface specification in the Get() method
even though I’m calling it via…an interface object. Can you say redundant?
No wonder Java programmers need IDEs.
I hope this situation improves. Unix used to stand for power through simplicity. Less code is less to break, and simple APIs leave developers free to worry more about bugs in their code than try to comprehend a needlessly complex API. Maybe I just don’t get it. These are first impressions, afterall, so perhaps I’m being overly judgemental. Perhaps there’s beauty and simplicity and elegance to be found in this overly wordy API requiring redundant information.
For now though, I just don’t see it.