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();
});