Control KDE applications from the commandline with D-Bus
Tagged:  •    •    •    •    •  

This is more or less a follow-up on my previous article, but this time we focus on how to communicate with KDE applications from the commandline. This is done with D-Bus and for that we'll use the qdbus command. For example, it's possible to request the current track information in Amraok, or you could set your Kopete status on Away.

D-Bus

But before we do this, we'll have a look at D-Bus itself. D-Bus is a mechanism to allow communication between applications (processes). Try to see it as a virtual network: the applications represent the nodes and D-Bus provides the infrastructure for the communication. On a desktop machine, D-Bus is primarily used by KDE, GNOME and the Hardware Abstraction Layer (HAL).

Back in the early days the landscape of inter-process communication was much more diverse. KDE 2 and 3 used DCOP (Desktop Communication Protocol). GNOME used a different mechanism: Bonobo.

The Qt toolkit offers D-Bus support from version 4. KDE 4 replaced DCOP with D-Bus and GNOME gradually started to adopt D-Bus in favor of Bonobo. So in theory, uniform communication is possible between these two platforms.

Qt 4 ships a little tool which allows you to communicate with all applications connected to D-Bus: qdbus. Those who used the dcop before the days of KDE 4 wouldn't experience much difficulty with getting familiar with qdbus.

Getting started with qdbus

Let's start with something simple: retrieving some metadata from the current track in Amarok 2.0.

When you run qdbus you'll see a list of names. These are all processes connected to D-Bus. KDE applications are to be recognized with the org.kde prefix and GNOME applications with the org.gnome prefix. If Amarok is running, you'll see org.kde.amarok. Let's execute the following command:

$ qdbus org.kde.amarok
/
/AudioOutputs
/AudioOutputs/0
/KBookmarkManager
/KBookmarkManager/amarok
/KBookmarkManager/kfilePlaces
/KDebug
/KIO
/KIO/Scheduler
/MainApplication
/Player
/TrackList
/amarok
/amarok/MainWindow
/amarok/MainWindow/actions
/amarok/MainWindow/actions/options_configure_keybinding
/amarok/MainWindow/actions/options_configure
/amarok/MainWindow/actions/file_quit
/internal
/internal/PhononXine

Amarok consists of several (KDE) components, each offering their own D-Bus interface. Let's examine one such interface more closely by appending its name to the previous command:

$ qdbus org.kde.amarok /Player
signal void org.freedesktop.MediaPlayer.CapsChange(int)
method int org.freedesktop.MediaPlayer.GetCaps()
method QVariantMap org.freedesktop.MediaPlayer.GetMetadata()
method void org.freedesktop.MediaPlayer.Next()
method void org.freedesktop.MediaPlayer.Pause()
method void org.freedesktop.MediaPlayer.Play()
method int org.freedesktop.MediaPlayer.PositionGet()
method void org.freedesktop.MediaPlayer.PositionSet(int)
method void org.freedesktop.MediaPlayer.Prev()
method void org.freedesktop.MediaPlayer.Repeat(bool)
method void org.freedesktop.MediaPlayer.Stop()
method int org.freedesktop.MediaPlayer.VolumeGet()
method void org.freedesktop.MediaPlayer.VolumeSet(int)
method QDBusVariant org.freedesktop.DBus.Properties.Get(QString interface_name, QString property_name)
method QVariantMap org.freedesktop.DBus.Properties.GetAll(QString interface_name)
method void org.freedesktop.DBus.Properties.Set(QString interface_name, QString property_name, QDBusVariant value)
method QString org.freedesktop.DBus.Introspectable.Introspect()

As you can see, an interface consists of several functions. Lines starting with method are callable from the commandline. So we wish to see some track information, and the function GetMetadata supplies this. So we append this function name (without parentheses) to the previous command:

$ qdbus org.kde.amarok /Player org.freedesktop.MediaPlayer.GetMetadata
album: No Line On The Horizon
artist: U2
arturl:
audio-bitrate: 198
audio-samplerate: 44100
comment:
genre: Rock
location: file:///home/bram/Muziek/U2-No_Line_On_The_Horizon/02-magnificent.mp3
mtime: 320000
rating: 0
time: 320
title: Magnificent
tracknumber: 2
year: 2009

So that's the track information we're looking for. There are also functions out there to change something in an application. Often it's necessary to supply some parameters. For example, let's look at the following function:

$ qdbus org.kde.amarok /TrackList org.freedesktop.MediaPlayer.SetLoop true

SetLoop accepts one parameters with a boolean value. If executed, Amarok will repeat the playlist over and over again.

Some handy commands

It's not really clear what possibilities are available with this trick. You have to know where to find certain functionality (if the application has a D-Bus interface at all). Therefore I'll give a short list of commands which should get you started. These commands were executed with KDE 4.2.2, but they should remain valid throughout the whole KDE 4 series.

  • Set the status to Away on all connected accounts in Kopete:
    qdbus org.kde.kopete /Kopete org.kde.Kopete.setOnlineStatus Away
  • Set the status back to Online on all connected accounts in Kopete:
    qdbus org.kde.kopete /Kopete org.kde.Kopete.setOnlineStatus Online
  • Set a status message in Kopete:
    qdbus org.kde.kopete /Kopete org.kde.Kopete.setStatusMessage "This status message is set thanks to D-Bus"
  • Let KMail check your email:
    qdbus org.kde.kmail /KMail org.kde.kmail.kmail.checkMail
  • Save all documents in Kate. Notice that this may cause a dialog to appear when a document has no filename yet. Also, be aware of the process ID in the process name. Be sure to know this number before you call it with qdbus.
    qdbus org.kde.kate-11494 /kate/__KateMainWindow_1/actions/file_save_all trigger

    Update Or, use the following command to save all files:

    qdbus | grep kate | while read k; do qdbus $k | grep save_all | while read
    i; do qdbus $k $i trigger; done; done

    Thanks, Federico.

  • To quit a KDE application (gracefully):
  • qdbus org.kde.naam /MainApplication quit
  • Shutdown your computer with a confirmation dialog for 30 seconds (be careful!):
    qdbus org.kde.ksmserver /KSMServer org.kde.KSMServerInterface.logout 1 2 2

Applications

You may wonder what's the use of typing such long commands, when a single button (or even a shortcut) does the same. But there are a couple of scenarios where these kind of commands come in handy.

  • SSH

    It could happen that you log into a machine with SSH, where KDE is running on the remote side. This way you can still control some KDE applications without having access to the X server (like saving your work or quit applications as described above). Personally I abused this with my dad's PC. I needed him to be online, so I logged into his PC, started Kopete and issued the right command to connect to his account.

    Be aware that this may not work immediately from the commandline, since qdbus requires the $DISPLAY variable to be properly set. It should be set to the display where X is currently active. Often the following command should work:

    export DISPLAY=:0
  • Scripts

    D-Bus is quite handy from scripts. The script below combines several commands given above. It retrieves the track information from Amarok 2 and composes a status message for Kopete. That way, people can see what you're listening:

    #!/bin/sh

    export DISPLAY=:0
    MESSAGE=""
    NOWPLAYING=`qdbus org.kde.amarok /Player org.freedesktop.MediaPlayer.GetMetadata`
    if [ $? = 0 ] && [ -n "$NOWPLAYING" ]; then
    ARTIST=`echo "$NOWPLAYING" | sed -ne 's/^artist: \(.*\)$/\1/p'`
    TRACK=`echo "$NOWPLAYING" | sed -ne 's/^title: \(.*\)$/\1/p'`
    MESSAGE="Listening to $ARTIST - $TRACK"
    fi

    qdbus org.kde.kopete /Kopete org.kde.Kopete.setStatusMessage "$MESSAGE"

    So the first thing we do is to retrieve the track information. With [ $? = 0 ] the return value of qdbus is checked. If Amarok is not running it will return 1. Then, the useful pieces of information is being filtered out and put into a string. This string is supplied to Kopete with D-Bus.

    To update your status automatically, you can extend this script with a while loop, or you could put this script in a minutely cron job.

  • Internal communication between KDE components

    Amarok offers an interface with a high degree of control for playing your tracks. This can be used to create a 'remote control', completely independent from Amarok. It is relatively easy to create a play and stop button for your taskbar.
    Something different emerged around Bluetooth enabled phones. When the Bluetooth daemon detected that the phone is out of range, it automatically switches your Kopete status to Away. This gives a nice illustration of the possibilities.

Conclusion

This concludes the introduction of using D-Bus and especially qdbus. The articles focussed on KDE applications, but the same idea can be used for different D-Bus enabled applications as well, like GNOME.

Update 11 april 2009:

A little while later I realized that Qt also installs a graphical D-Bus browser: qdbusviewer. It does not offer more features than qdbus, but it makes exploring the possibilities much easier.

KDE4 script to log in to several servers at once in tabs

Frequently I need to log on to several servers (i.e. ssh to several boxes). In KDE3.5 I used DCOP for this --- but alas KDE 4 uses qdbus. Could not find how to do this on the web, so including it here. Basically this script has a list of boxes (in $blist), displays a menu allowing you to select which boxes to ssh to, then opens a tab, and ssh's to that box
THE SCRIPT
------------------------
MYCOMMAND='ssh '
blist="serverBox1.example.com serverBox2.example.com serverBox3.example.com serverBox4.example.com"
for i in $blist ; do
menulist="$menulist $i $i on"
done
boxlist=$(kdialog --separate-output --checklist "Select all boxes to open" $menulist)
cursession=`qdbus org.kde.konsole /Konsole currentSession`
for i in $boxlist
do
echo $i
session=$(qdbus org.kde.konsole /Konsole newSession)
qdbus org.kde.konsole /Sessions/${session} setTitle 1 $i
qdbus org.kde.konsole /Sessions/${session} sendText "${MYCOMMAND}"
qdbus org.kde.konsole /Sessions/${session} sendText "${i}"
qdbus org.kde.konsole /Sessions/${session} sendText "
"
qdbus org.kde.konsole /Sessions/${session} sendText "
"
qdbus org.kde.konsole /Sessions/${session} sendText "echo Running on ${i}
"
qdbus org.kde.konsole /Sessions/${session} setMonitorActivity true
done

-------------
END OF SCRIPT