2009-01-24

using d-bus: an example

In the previous entry, I mentioned Emacs version 23. This time, let's see what we can do with one of its new features: D-Bus-support. As you might know, D-Bus is an IPC system used on Linux/Unix to communicate between applications; it's a kind-of successor to systems like Bonobo (Gnome) and KParts (KDE). With support for D-Bus in Emacs, it's easy to control other applications. More and more programs are providing D-Bus interfaces.

As a semi-useful example of that, let's look at using D-Bus to control the Tomboy Note Taker application. Tomboy takes a novel approach (at least for me) at note taking -- your notes are connected like a wiki. Tomboy was originally designed and written by Alex Graveley, who has a talent for coming up with interesting ideas, and then turning them into software. Anyhow, I only use Tomboy as example of the use of D-Bus from emacs; there are many other interesting programs controllable through D-Bus.

Note, I won't discuss the details of D-Bus itself here; there's a lot of good material available already. Specifically, for some more background on using D-Bus with emacs, I can recommend the documentation (info-page) that comes with Emacs-23.

Back to Tomboy. First, let's make it easy to call Tomboy. It obviously assumes you have Tomboy installed, but it does not have to be running. First, a helper function that uses the new dbus-call-method-function, sets the service, path and interface names for Tomboy; this way, we don't have to specify them for each call:

(require 'dbus)
(defun djcb-call-tomboy (method &rest args)
  "call the tomboy method METHOD with ARGS over dbus"
  (apply 'dbus-call-method 
    :session                            ; use the session (not system) bus
    "org.gnome.Tomboy"                  ; service name
    "/org/gnome/Tomboy/RemoteControl"   ; path name
    "org.gnome.Tomboy.RemoteControl"    ; interface name
    method args))

Then, djcb-tomboy-create-note-region creates a new note from the region (selection) use CreateNamedNote and SetNoteContents; it even does some rudimentary error checking:

(defun djcb-tomboy-create-note-region (b e name)
  "Create a new note with in the Tomboy notetaker from region"
  (interactive "r\nsName for new Tomboy note:")
  (let ((note-uri (djcb-call-tomboy "CreateNamedNote" name)))
    (if (and note-uri (> (length note-uri) 0))
      (djcb-call-tomboy "SetNoteContents" note-uri 
        (concat name "\n" (buffer-substring b e)))
      (message "hmmm... it did not work. maybe try a different name"))))
With djcb-tomboy-insert-note-contents we can insert the contents of some tomboy note into the current buffer, using FindNote/GetNoteContents There's auto-completion available for the name of the note, using ListAllNotes:
(defun djcb-tomboy-insert-note-contents (name)
  "Insert Tomboy note with NAME"
  (interactive 
    (list (let ((lst))
            (dolist (uri (djcb-call-tomboy "ListAllNotes"))
              (add-to-list 'lst (djcb-call-tomboy "GetNoteTitle" uri)))
            (completing-read "Name of Tomboy Note:" lst))))
  (let ((note-uri (djcb-call-tomboy "FindNote" name)))
    (when note-uri
      (insert (djcb-call-tomboy "GetNoteContents" note-uri)))))

So, easy enough to do, even for an Elisp novice like myself. Again, this is more to show the use of dbus from emacs than about Tomboy itself -- but it is somewhat useful. I'm actually not using Tomboy so much anymore - org-mode better fits my emacs-centric workflow. But Tomboy has some very interesting plugins which might be nice for org-mode as well.

As I said before, Tomboy is only an example here -- there are many other D-Bus services available ('dbus-list-activatable-names') And D-Bus services are introspectable - you can search through them, and retrieve information about the interfaces they provide, as well as the methods and signatures. Again, the Emacs D-Bus infopages are quite useful.

5 comments:

Dmitry said...

Hi!

I try to use dbus but emacs (emacs-snapshot on ubuntu-8.04) stop react on anything I do after
(require 'dbus)
so I have to close it...
Should I run emacs with any special option to use dbus from it? Or it works only if I build emacs from source with option --with-dbus ?

djcb said...

@Dmitry: no idea... i am using emacs-snapshot on ubuntu 8.10. even if the 8.04 version was not compiled with --with-dbus, but it shouldn't hang.

Dmitry said...

I found the error: dbus wasn't running. I change my ~/.xinitrc from

/usr/bin/local/stumpwm

to

dbus-launch /usr/bin/local/stumpwm

now (require 'dbus) works fine.

Now I have another question:

after start

(dbus-list-known-names :session)

returns just

("org.freedesktop.DBus")

how can I start other services?

Dirk-Jan said...

@Dmitry: if you have installed any programs that support dbus, they should be available when you start dbus.

dbus should know about these services because of their '.service'-files, somewhere under /usr/share/dbus-1

Dmitry said...

Awesome! :) We can use dbus to show notifications (org.freedesktop.Notifications) on different events, for example on incomming jabber messages.