Skip to main content

Ralsina.Me — Roberto Alsina's website

DBUS-reactor, or AsusOSD must die!

I love my Asus eee 701. I love it so much be­cause I use it ev­ery day. One nice thing about it is that since it came with Lin­ux, ev­ery­thing in it is well sup­port­ed. Specif­i­cal­ly, all keys do what they are sup­posed to do, and do it nice­ly.

When you click "vol­ume up" the vol­ume not on­ly goes up, but you al­so get a graph­ic on screen telling you so and show­ing the cur­rent vol­ume. The ap­pli­ca­tion that shows that is called asu­sos­d.

How­ev­er, once you turn your eee in­to a reg­u­lar ma­chine re­mov­ing the Xan­dros Lin­ux that came with it, since ev­ery­thing is well sup­port­ed, strange things hap­pen.

For ex­am­ple, when you click on "Vol­ume Up­", you get two no­ti­fi­ca­tions on screen: asu­sosd and kmix. Since kmix is much more use­ful than asu­sos­d, the ob­vi­ous way to fix this is re­mov­ing asu­sos­d.

But... that means oth­er no­ti­fi­ca­tions are now gone. For ex­am­ple, "Bright­ness Up" does what it's sup­posed to do, but you get no on-screen no­ti­fi­ca­tion (other than the screen be­ing brighter ;-).

On oth­er note­books sim­i­lar things hap­pen: I have a HP Pavil­ion, and the "WWW" key does noth­ing.

So, what does a hack­er do? He hack­s. What do I do? I hack with python. Keep in mind, from now on, that I don't re­al­ly know what heck I am do­ing, this is most­ly a blind hack, but I think it may work as ex­am­ple code for the next guy stum­bling in­to the python+d­bus+hal comp­bi­na­tion.

Here's how it work­s:

When­ev­er you press a "spe­cial key", HAL will send a DBUS mes­sage about it. You can see that us­ing dbus-­mon­i­tor. This is what hap­pens when I press vol­ume-­down in my HP:

$ dbus-monitor --system
signal sender=org.freedesktop.DBus -> dest=:1.144 serial=2
path=/org/freedesktop/DBus; interface=org.freedesktop.DBus;
member=NameAcquired
string ":1.144"
signal sender=:1.0 -> dest=(null destination) serial=5365
path=/org/freedesktop/Hal/devices/platform_i8042_i8042_KBD_port_logicaldev_input;
interface=org.freedesktop.Hal.Device; member=Condition
string "ButtonPressed"
string "volume-down"

You can cre­ate a python pro­gram, us­ing python-d­bus, that con­nects to those events, and re­acts in con­se­quence (thus the dbus-re­ac­tor name, ok?)

There is a very an­noy­ing prob­lem here, though. This is the same event in my eee:

$ dbus-monitor --system
signal sender=org.freedesktop.DBus -> dest=:1.22 serial=2
path=/org/freedesktop/DBus; interface=org.freedesktop.DBus;
member=NameAcquired
string ":1.22"
signal sender=:1.0 -> dest=(null destination) serial=632
path=/org/freedesktop/Hal/devices/computer_logicaldev_input_3;
interface=org.freedesktop.Hal.Device; member=Condition
string "ButtonPressed"
string "volume-down"

While the two are quite alike, the path pa­ram­e­ter is dif­fer­en­t, so that can't be used, and ev­ery ex­am­ple I found of python-d­bus us­age ... us­es path.

It would be nicer if my pro­gram worked in both note­book­s, though. It turns out that it's not all that hard. The trick is in ask­ing your sys­tem what the key­board is called. To do that, you call the "Find­By­De­vice­Ca­pa­bil­i­ty" method of the HAL man­ag­er. Here's a snip­pet that does just that [list­ing1.py]:

import sys
import dbus

bus = dbus.SystemBus()
proxy = bus.get_object('org.freedesktop.Hal', '/org/freedesktop/Hal/Manager')
interface = dbus.Interface(proxy,dbus_interface='org.freedesktop.Hal.Manager')
print interface.FindDeviceByCapability('input.keyboard')[0]

Try it, it should work on any mod­ern Lin­ux sys­tem run­ning HAL!

$ python listing1.py
/org/freedesktop/Hal/devices/platform_i8042_i8042_KBD_port_logicaldev_input

So, now, let's start with a very sim­ple pro­gram that sets up all the DBUS stuff and con­nects to the 'Con­di­tion' sig­nal [list­ing2.py].

# -*- coding: utf-8 -*-

import sys
import dbus
import dbus.mainloop.qt
from PyQt4 import QtCore

app=QtCore.QCoreApplication(sys.argv)

# You **must** set the event loop before getting the bus,
# or connecting to signals will not work. Funny, isn't it?

mainloop=dbus.mainloop.qt.DBusQtMainLoop(set_as_default=True)
bus = dbus.SystemBus()

hal_proxy = bus.get_object('org.freedesktop.Hal', '/org/freedesktop/Hal/Manager')
manager = dbus.Interface(hal_proxy,dbus_interface='org.freedesktop.Hal.Manager')
kbd_dev = manager.FindDeviceByCapability('input.keyboard')[0]

kbd=dbus.Interface( bus.get_object('org.freedesktop.Hal', kbd_dev),
    dbus_interface='org.freedesktop.Hal.Device')

dbus.set_default_main_loop(mainloop)

def handler(*args,**kwargs):
    print "got signal from %s" % kwargs
    print kwargs['message'].get_args_list()

def setHook():
    kbd.connect_to_signal('Condition',handler,sender_keyword='sender',
        destination_keyword='dest',
        interface_keyword='interface',
        member_keyword='member',
        path_keyword='path',
        message_keyword='message')

setHook()
print 'Entering loop'
app.exec_()

When I run it, I get this:

$ python listing2.py
Entering loop
got signal from {'sender': ':1.0', 'dest': None, 'member': 'Condition',
'interface': 'org.freedesktop.Hal.Device',
'path': dbus.ObjectPath('/org/freedesktop/Hal/devices/platform_i8042_i8042_KBD_port_logicaldev_input'),
'message': <dbus.lowlevel.SignalMessage object at 0xb75296a0>}
[dbus.String(u'ButtonPressed'), dbus.String(u'volume-down')]

So, there you go, this pro­gram is no­ti­fied of the mag­ic keys al­ready!

Now, how can you make pret­ty on-screen no­ti­fi­ca­tion­s? It turns out DBUS takes care of that, too, via the ses­sion bus and org.freedesk­top.No­ti­fi­ca­tions in­ter­face. Here is a snip­pet to show a mes­sage [list­ing3.py]:

import sys
import dbus

# This uses the session bus because it's session-specific.
bus = dbus.SessionBus()
proxy = bus.get_object('org.freedesktop.Notifications', '/org/freedesktop/Notifications')
interface = dbus.Interface(proxy,dbus_interface='org.freedesktop.Notifications')

# This should display title and message, with no icon, both in KDE and GNOME
interface.Notify('test_runner', 0, '', 'title', 'message', [], {}, -1)

So, let's do an­oth­er it­er­a­tion of the main pro­gram, so that now we are no­ti­fied when a mag­ic key is pressed (luck­i­ly, reg­u­lar keys cause no HAL events! ;-) [list­ing4.py]

import sys
import dbus
import dbus.mainloop.qt
from PyQt4 import QtCore

app=QtCore.QCoreApplication(sys.argv)
mainloop=dbus.mainloop.qt.DBusQtMainLoop(set_as_default=True)
systemBus = dbus.SystemBus()
sessionBus = dbus.SessionBus()

notify_proxy = sessionBus.get_object('org.freedesktop.Notifications', '/org/freedesktop/Notifications')
notifier = dbus.Interface(notify_proxy,dbus_interface='org.freedesktop.Notifications')

hal_proxy = systemBus.get_object('org.freedesktop.Hal', '/org/freedesktop/Hal/Manager')
manager = dbus.Interface(hal_proxy,dbus_interface='org.freedesktop.Hal.Manager')
kbd_dev = manager.FindDeviceByCapability('input.keyboard')[0]

kbd=dbus.Interface( systemBus.get_object('org.freedesktop.Hal', kbd_dev),
    dbus_interface='org.freedesktop.Hal.Device')

dbus.set_default_main_loop(mainloop)

def handler(*args,**kwargs):
    print "got signal from %s" % kwargs
    title=kwargs['message'].get_args_list()[1].replace('-',' ').title()
    notifier.Notify('System',0,'',title,'',[],{},-1)
    print kwargs['message'].get_args_list()

def setHook():
    kbd.connect_to_signal('Condition',handler,sender_keyword='sender',
        destination_keyword='dest',
        interface_keyword='interface',
        member_keyword='member',
        path_keyword='path',
        message_keyword='message')

setHook()
print 'Entering loop'
app.exec_()

And here's what hap­pens when I press the spe­cial "Lock" key on my key­board:

reactor1

It's triv­ial to make this pro­gram much bet­ter:

  • Han­­dle mul­ti­­ple key­board­­s.

  • Adding icon sup­­port for no­ti­­fi­­ca­­tion­s.

  • The pos­si­­bil­i­­ty to ig­nore spe­­cif­ic keys (like Vol­ume Up­­/­­Down/­­Mute, which are nice­­ly han­­dled by KMix)

  • Sup­­port for launch­ing a pro­­gram in­­stead of no­ti­­fy­ing.

  • Re­act to events oth­­er than key­board (ex­am­­ple: plug­ging/un­­plug­ging of the AC pow­er)

  • Con­­fig­u­ra­­tion!

  • Sup­­port Qt or glib event loops

I may (or may not) do it, so if any­one wants to, it's a fun be­gin­ner's pro­jec­t! In less than a mon­th, you may have a nice, use­ful pro­gram in your hand­s. And then comes fame and for­tune, of course.

Vibram / 2010-08-31 03:07:

Thanks for sharing


Contents © 2000-2024 Roberto Alsina