GNOME 3 Plugins: DBus

I’m currently messing around with GNOME 3 plugins, and there’s not a whole lot of information out there.  Like, almost nothing.  So, as I’m working on this stuff I will be posting up some information and some small plugins that demonstrate a certain feature.  Whenever possible, I will also link to the relevant code or library function(or at least what I think the proper library/function is).

Now, before we get started, note that this information is for GNOME shell 3.8.4, which is what Debian 8 ships with, as well as Red Hat Enterprise Linux(RHEL) 7.  Also, a note to the GNOME devs: WRITE SOME DOCUMENTATION.  Seriously.  Even a generated API document that shows what methods are on each object in JavaScript would be better than what currently is available(re: nothing).

Note that this is also going to be a bit of a crash course in JavaScript for me, so I may say some completely incorrect things about JavaScript.  If I make any errors, please feel free to leave a comment to correct me.  If you could also put a source with that, that would be very helpful so that we can all learn.

Alright, now let’s get onto the examples!

Create a new Extension

This is covered in most of the tutorials already, but we of course need an extension to edit!  So, let’s create a new one with:

gnome-shell-extension-tool --create-extension

Now give it a name, description and UUID. You can see an example here.  The new extension will be in

~/.local/share/gnome-shell/extensions/<your-extension-id>

Once you have edited an extension, you will have to reload the gnome-shell in order for the changes to take effect.  To do this, do ALT+F2, type ‘r’, and hit enter.

To view the output of the console, you will have to use systemd’s journalctl(for Debian 8, and probably RHEL 7).  The command is:

journalctl /usr/bin/gnome-session -f -o cat

Source here.

Also, you can enable-disable extensions using gnome-tweak-tool.

Example 1: DBus Connections

If you’re unfamiliar with DBus concepts, I recommend that you take a look at my DBus tutorial.  The first thing that we are going to have to do is to define an XML interface(essentially, what the DBus Introspection method gives back).

So, using our old friend the DBus tutorial, we will create an interface that looks suspiciously similar to our first DBus example of simply echoing a string out to the console. Here’s the interface XML:

<interface name="com.rm5248.ReceiveInterface">
<method name="echoMessage">
    <arg type="s" direction="in" name="message"/>
</method>
</interface>

For each method that we put on the bus, we must have a method on our javascript object that has the same name.  If we don’t, we will get an error about there not being a method to call.

Here’s the important part that actually connects to the bus(DBusIface refers to the XML that we defined above):

    this._dbusImpl = Gio.DBusExportedObject.wrapJSObject( DBusIface, this );
    this._dbusImpl.export( Gio.DBus.session, '/' );

You can check to see that this method works using qdbus:

qdbus org.gnome.Shell / com.rm5248.ReceiveInterface.echoMessage hithere

You should now see the log printed out with your message.

This is all well and good, but what if we want to have our own DBus address?  We don’t want everything to be under the org.gnome.Shell address. To do that, we simple need to acquire the well-known name on the bus that we want to use:

this._dbusId = Gio.DBus.session.own_name( 'com.rm5248', Gio.BusNameOwnerFlags.NONE, this._nameAcquired, this._nameLost );

A note on this though: your object will still be accessible from the well-known bus names of ‘com.rm5248’ and ‘org.gnome.Shell’.  Essentially, ‘com.rm5248’ becomes an alias for ‘org.gnome.Shell’, so any path that you can access using ‘org.gnome.Shell’ you can also use ‘com.rm5248’.  This perhaps not a bug, but it is contrary to how most bindings work.

The source that I used for testing can be found here. I’ve commented as much as I can, given my knowledge of how it works.

Example 2: Sending Messages

Now that we have our connection on the bus, and we can receive messages, how do we send messages out?  Well, we need to do two things: Create a proxy, and then create an instance of that proxy.  Once we have the proxy, we can call the methods asynchronously or synchronously.  Here’s the important part of the code(note that DBusIface is the same XML that we defined earlier):

   
    const ReceiveInterface = Gio.DBusProxy.makeProxyWrapper( DBusIface );
    var prox = new ReceiveInterface( Gio.DBus.session, 'com.rm5248', '/', 
        // I don't know what this Lang.bind does, but it's very important(the
        // plugin will load very slowly if it isn't here)
        Lang.bind( this, function( proxy, error ){
            if( error ) {
                log('error: ' + error );
                return;
            }
        })
    );

    try{
        // There are two methods that are made on the javascript object: <name>Sync and <name>Remote.
        // <name>Sync calls the method synchronously, <name>Remote calls it async.  
        //  I don't know where the error message goes when you call it async; a message is printed
        // out in the console that the exception was ignored.
        prox.echoMessageSync( "hithere" );
    }catch( e ){
        // This could fail if the well-known bus name does not exist
        log( 'Error calling method: ' + e );
    }

So, now we can call methods on other objects.  You can get the source code for this example here.  Also, a note on calling methods: there appears to be a bug when calling with the ‘sync’ method, in that it will take a long time to return.  I will do some more investigation.

Coming up in our next tutorial: settings.

Leave a Reply

Your email address will not be published. Required fields are marked *