1473028762
kd@ 9/4/2016

GTK & GJS

I’m going to make a hash of this, because I’m not really a desktop developer, but if you squint, the details are going to be correct. GTK is the Gnome project’s graphical toolkit, the set of libraries that let you write Gnome-y applications. Gjs is the name of the official Gnome Javascript toolkit- a set of libraries bundled up with Spidermonkey, Mozilla’s Javascript runtime. Gnome Shell, Gnome Documents, and some other things are written with it. If you want to write Gnome apps, it’s a pretty reasonable place to start- especially since you probably already know a little Javascript.

Javascript also fits in well with GTK because they’re both built around an asynchronous programming model. There is a big, main loop that you can’t see, but you can feed in functions to get executed in the future. As an example, let’s write a Gnome-ecosystem thing to make HTTP calls.

#!/usr/bin/env gjs
const Soup = imports.gi.Soup; // Gnome's HTTP library is 'libsoup'

var request = Soup.Message.new('GET', 'http://example.org');

var session = new Soup.SessionAsync();

session.queue_message(request, function() { log("done"); });

So that should be fairly readable- we want to import the HTTP library, we ask it to make an HTTP call, and we put it on a queue to get executed by main loop, the thing driving the application, and when it’s done, our callback will fire. Not zero boilerplate, but reasonable. So let’s run it!

# ./get.js 
# 

Underwhelming. There’s a simple thing we’re missing though- remember the “main loop” I was talking about earlier? We’re writing a stand a lone program, and not a library, which means that we have to, at some point, tell Gjs to run a main loop. This is an easy fix:

session.queue_message(request, function() { log("done"); });

const Mainloop = imports.mainloop;
Mainloop.run();

and now we run it:

# ./miniget.js 
Gjs-Message: JS LOG: done
^C

Cool! Everything is happening like we’d hoped, but then it just sits there, looping forever. So let’s tell it to quit when done:

#!/usr/bin/env gjs
const Soup = imports.gi.Soup; // Gnome's HTTP library is 'libsoup'
var request = Soup.Message.new('GET', 'http://example.org')
var session = new Soup.SessionAsync();
const Mainloop = imports.mainloop;
session.queue_message(request, function() { log("done"); Mainloop.quit(); });
Mainloop.run();

and now when we run it:

# ./miniget.js 
Gjs-Message: JS LOG: done

So this is great, but there’s definitely something missing- the content returned by the server, maybe.

session.queue_message(request, function(session, message) {
log(message.response_body.data);
log("HTTP status code " + message.status_code);
log("done");
Mainloop.quit();
});