I have been a hobby hacker for my entire adult life, and a bit before that too. When your profession is making software, or even making open source software, the joy from hobby-hacking can diminish or even disappear. One of the things I learned from burning out was that, if I am going to continue to enjoy hacking as a personal hobby, I would need to pursue “frivolous hacking.”
Hacking on something for no practical or otherwise useful reason.
Late last week I was struck with the desire to play around with GtkAda. Ada has long been a hobby language for me, but I have never gotten the desire to make a GUI-based application. For this weekend’s frivolous hacking, I created arun a simple Ada Command Runner with GtkAda and Glade 3.
The primary reason to use Glade, in my opinion, is that it’s 2017 and hand-coding interface components is beyond tedious. At a high level, Glade provides a WYSIWYG approach for defining the various interface components a program requires, exports that as an XML file which can later be loaded by the program at runtime.
My first attempt relied on using the Glade3 and GtkAda packages which exist for
Debian-based systems. Unfortunately
libgtkada2.24.4-dev binds to Gtk+ version
2, rather than Gtk+ version 3, which Glade3 targets.
Instead of native packages, you can also download GtkAda from
AdaCore directly. Opting for this route I was
disappointed to learn that the file named
isn’t actually a set of binaries but just a source tarball which must be built
and installed. At first, that didn’t work for me
either! A bug in
that source tarball leaves a user like me with one of two options:
- Configure the installation with
- Grab the latest (
fc8b2f9at this time) from GitHub:
I opted for the latter and within short order (
make -j4!) I had fresh
installation of GtkAda on my system.
Writing an app
Fortunately the Ada DK wiki has an example I could start from, which covers the basics of building a GUI with Glade3 in GtkAda. After cribbing some Ada code from the wiki page, I found another stumbling block, it wouldn’t compile.
Turns out some APIs had changed since the page was originally written, but as luck would have it Stack Overflow had the answer.
Without going into too much detail on how to use GtkAda,
Arun.Main procedure demonstrates a basic version of loading and
starting a Glade3-based GtkAda app:
Gtk.Main.Init; Gtk_New (Builder); -- Load `arun.glade` which contains the interface definition Return_Code := Add_From_File (Builder => Builder, Filename => "arun.glade", Error => Error'Access); if Error /= null then Put_Line ("Error : " & Get_Message (Error)); Error_Free (Error); return; end if; -- Register a signal handler for when -- the "Main_Quit" signal is fired Register_Handler (Builder => Builder, Handler_Name => "Main_Quit", Handler => Arun.Handlers.Quit'Access); Do_Connect (Builder); -- Get the Gtk_Widget object from the Gtkada.Builder API -- for our commandWindow so it will be shown. Gtk.Widget.Show_All ( Gtk_Widget (Gtkada.Builder.Get_Object (Builder, "commandWindow"))); Gtk.Main.Main; Unref (Builder);
arun binary compiled, I decided to try it out:
obj % ./arun Starting arun Error : Failed to open file 'arun.glade': No such file or directory obj %
As one might expect, there is a downside to loading files at executable runtime.
Another Stack Overflow answer introduced me
glib-compile-resources executable, which provides the solution
for loading the
.glade file at runtime in a more portable way. Instead of
referring to a
glib-compile-resources creates a C file which
can be compiled and linked into the application. Of course, this adds some more
build-time complexity, as
glib-compile-resources needs to be called, the C
file needs to be compiled, and then the object file must be linked into the
With the Makefile put together, instead of
Add_From_File the application uses
Add_From_Resource as is demonstrated in this commit.
With a resource the binary can then be distributed without the
obj % ./arun Starting arun Searching for x Searching for xeye Searching for xeyes Looking for an executable in /home/tyler/.rvm/gems/ruby-2.1.5/bin Looking for an executable in /home/tyler/.rvm/gems/ruby-2.1.5@global/bin Looking for an executable in /home/tyler/.rvm/rubies/ruby-2.1.5/bin Looking for an executable in /home/tyler/bin Looking for an executable in /home/tyler/scratch/bin Looking for an executable in /home/tyler/.rvm/bin Looking for an executable in /home/tyler/scratch/node_modules/.bin Looking for an executable in /home/tyler/scratch/ada/gnat-gpl-2016-x86_64-linux-bin/bin Looking for an executable in /home/tyler/scratch/ada/spark-gpl-2016-x86_64-linux-bin/bin Looking for an executable in /home/tyler/bin Looking for an executable in /usr/local/bin Looking for an executable in /usr/bin Found executable at /usr/bin/xeyes Should Execute: xeyes Spawning /usr/bin/xeyes obj %
Suffice it to say, using GtkAda isn’t difficult per se, it’s only difficult to use when there are toolchain specific idiosyncrasies that are hard to Google. Aside from that challenge, I far prefer writing GtkAda rather than “pure” Gtk+ with C or C++.
Of course I don’t expect anybody else to find my code useful, nor do I have lofty ambitions about it, thereby meeting my “frivolous hacking” requirement!