Last week I landed a new feature in Glib that I’ve been wanting to do for a long time: A resource framework. Resources are things that are naturally part of an application or library, but not really normal code. For instance, our code increasingly uses xml to describe user interfaces and menus.
Traditionally these either had to be manually inserted into the code, like so:
static const gchar *ui_info = "<ui>" " <menubar name='MenuBar'>" " <menu action='FileMenu'>" " <menuitem action='Quit'/>" " </menu>" " </menubar>" "</ui>";
Or a file was stored in /usr/share/$application/ and you had to write code to manually find and load the file, and cache it if used often. This is not a lot of code, but it can be tricky as all I/O code needs to handle errors and the external file makes it harder to make the library/app relocatable.
Instead, with resources you store your data file as plain files in your source tree, edit them with your favourite editor, with full syntax highlighting, automatic indentation, etc. Then you reference these files from a resource description file (another xml file) and use glib-compile-resources to compile these into a binary chunk which is linked into the application.
The resource framework then automatically registers all such resource bundles in a global namespace, where you can quickly look up resource data by a resource path. There are API calls to get the data as a direct pointer, as well as streaming data, but in most cases there are helper functions that let you just specify the pathname. So, for instance, you can just do:
gtk_builder_add_from_resource (builder, "/org/gnome/appname/menu.ui", &error);
Which would handle all the work for you. And while this looks like an I/O operation its really just a hashtable lookup on linked-in data, reading from the (shared, readonly) data section in your executable, so its very fast and safe.
Additionally there are some tricks the resource compiler can do for you. For instance, you can specify that a resource should be compressed, which means that the data is stored compressed, and the APIs uncompress for you automatically. You can also specify that xml files should be pre-processed to strip away whitespace, which avoids wasting time and memory on something that is not useful at runtime.
There is also support for resource:// URIs, which means you can easily reference resource data like icons from e.g. CSS and UI files.
On Linux we use some gcc specific extensions to store the resources in separate ELF sections, which means its very easy to extract resource data from the binaries. Glib even ships with a tool that lets you do this:
$ gresource list libgtk-3.so /org/gtk/libgtk/cursor/dnd-ask.png /org/gtk/libgtk/cursor/dnd-copy.png /org/gtk/libgtk/cursor/dnd-link.png /org/gtk/libgtk/cursor/dnd-move.png /org/gtk/libgtk/cursor/dnd-none.png /org/gtk/libgtk/gtk-default.css /org/gtk/libgtk/gtk-win32.css $ gresource extract libgtk-3.so /org/gtk/libgtk/gtk-default.css @define-color fg_color #000; @define-color bg_color #dcdad5; @define-color text_color #000; ...