halting problemhttps://www.bassi.io/2023-10-25T09:43:52+01:00Introspection’s edge2023-10-25T09:31:00+01:002023-10-25T09:43:52+01:00ebassitag:www.bassi.io,2023-10-25:/articles/2023/10/25/introspections-edge/<p>In which I go over the plans for gobject-introspection</p><p><strong>tl;dr</strong> The introspection data for GLib (and its sub-libraries) is now
generated by GLib itself instead of gobject-introspection; and in the near
future, libgirepository is going to be part of GLib as well. Yes, there’s a
circular dependency, but there are plans for dealing with that. Developers
of language bindings should reach out to the GLib maintainers for future planning.</p>
<hr>
<p>GLib-the-project is made of the following components:</p>
<ul>
<li>glib, the base data types and C portability library</li>
<li>gmodule, a portable loadable module <span class="caps">API</span></li>
<li>gobject, the object oriented run time type system</li>
<li>gio, a grab bag of I/O, <span class="caps">IPC</span>, file, network, settings, etc</li>
</ul>
<aside><p>There are, of course, additional bits inside gobject-introspection,
like the regression and marshalling test suites, as well as the documentation
generators that very few people use.</p></aside>
<p>Just like GLib, the gobject-introspection project is actually three
components in a trench coat:</p>
<ul>
<li><code>g-ir-scanner</code>, the Python and flex based parser that generates the <span class="caps">XML</span>
description of a GObject-based <span class="caps">ABI</span></li>
<li><code>g-ir-compiler</code>, the “compiler” that turns <span class="caps">XML</span> into an <code>mmap()</code>-able binary format</li>
<li>libgirepository, the C <span class="caps">API</span> for loading the binary format, resolving
types and symbols, and calling into the underlying C library</li>
</ul>
<p>Since gobject-introspection depends on GLib for its implementation, the
introspection data for GLib has been, until now, generated during the
gobject-introspection build. This has proven to be extremely messy:</p>
<ul>
<li>as the introspection data is generated from the source code,
gobject-introspection needs to have access to the GLib headers and source files</li>
<li>this means building against an installed copy of GLib for the
declarations, and the GLib sources for the docblocks</li>
<li>to avoid having access to the sources at all times, there is a small
script that extracts all the docblocks from a GLib checkout; this script
has to be run manually, and its results committed to the
gobject-introspection repository</li>
</ul>
<p>This means that the gobject-introspection maintainers have to manually keep
the GLib data in sync; that any change done to GLib’s annotations and
documentation are updated typically at the end of the development cycle,
which also means that any issue with the annotations is discovered almost
always too late; and that any and all updates to the GLib introspection data
require a gobject-introspection release to end up inside downstream distributions.</p>
<p>The gobject-introspection build <em>can</em> use GLib as a subproject, at the cost
of some awful, <em>awful</em> build system messiness, but that does not help
anybody working on GLib; and, of course, since gobject-introspection depends
on GLib, we cannot build the former as a subproject of the latter.</p>
<p>As part of the push towards moving GLib’s <span class="caps">API</span> references towards the use of
<a href="https://gitlab.gnome.org/GNOME/gi-docgen">gi-docgen</a>, GLib now checks for
the availability of <code>g-ir-scanner</code> and <code>g-ir-compiler</code> on the installed system,
and if they are present, they are used to generate the GLib, GObject,
GModule, and <span class="caps">GIO</span> introspection data.</p>
<p>A good side benefit of having the actual project providing the <span class="caps">ABI</span> be in
charge of generating its machine-readable description is that we can now
decouple the release cycles of GLib and gobject-introspection; updates to
the introspection data of GLib will be released alongside GLib, while the
version of gobject-introspection can be updated whenever there’s new <span class="caps">API</span>
inside libgirepository, instead of requiring a bump at every cycle to match GLib.</p>
<p>On the other hand, though, this change introduces a circular dependency
between GLib and gobject-introspection; this is less than ideal, and though
circular dependencies are a fact of life for whoever builds and packages
complex software, it’d be better to avoid them; this means two options:</p>
<ul>
<li>merging gobject-introspection into GLib</li>
<li>making gobject-introspection not depend on GLib</li>
</ul>
<aside>
<p>This was the original plan for gobject-introspection, back in 2007-2009;
after iterating over a few cycles on the features required by language bindings,
the introspection format and <span class="caps">API</span> would be part of GLib, alongside the rest
of the type system. Best laid plans, and all that…</p>
</aside>
<p>Sadly, the <a href="https://gitlab.gnome.org/GNOME/glib/-/issues/2616">first option isn’t really
workable</a>, as it would
mean making GLib depend on flex, bison, and CPython in order to build
<code>g-ir-scanner</code>, or (worse) rewriting <code>g-ir-scanner</code> in C. There’s also the
option of rewriting the C parser in pure Python, but that, too, isn’t
exactly
<a href="https://gitlab.gnome.org/GNOME/gobject-introspection/-/merge_requests/231">strife-free</a>.</p>
<p>The second option is more convoluted, but it has the advantage of being
realistically implemented:</p>
<ol>
<li><a href="https://gitlab.gnome.org/GNOME/glib/-/merge_requests/3642">move libgirepository into GLib as a new sub-library</a></li>
<li>move <code>g-ir-compiler</code> into GLib’s tools</li>
<li>copy-paste the basic GLib data types used by the introspection scanner
into gobject-introspection</li>
</ol>
<p>Aside from breaking the circular dependency, this allows language bindings
that depend on libgirepository’s <span class="caps">API</span> (Python, JavaScript, Perl, etc) to
limit their dependency to GLib, since they don’t need to deal with the
introspection parser bits; it also affords us the ability to clean up the
libgirepository <span class="caps">API</span>, provide better documentation, and formalise the file formats.</p>
<p>Speaking of file formats, and to manage expectations: I don’t plan to change
the binary data layout; yes, we’re close to saturation when it comes to
padding bits and fields, but there hasn’t been a real case in which this has
been problematic. The <span class="caps">API</span>, on the other hand, really needs to be cleaned up,
because it got badly namespaced when we originally thought it could be moved
into GLib and then the effort was abandoned. This means that libgirepository
will be bumped to 2.0 (matching the rest of GLib’s sub-libraries), and will
require some work in the language bindings that consume it. For that,
bindings developers should get in touch with GLib maintainers, so we can
plan the migration appropriately.</p>The Mirror2023-08-23T21:23:00+01:002023-08-23T21:23:57+01:00ebassitag:www.bassi.io,2023-08-23:/articles/2023/08/23/the-mirror/<p>In which I go down the rabbit hole of the GObject type system design and a possible future</p><p>The GObject type system has been serving the <span class="caps">GNOME</span> community for more than 20 years. We have based an entire application development platform on top of the features it provides, and the rules that it enforces; we have integrated multiple programming languages on top of that, and in doing so, we expanded the scope of the <span class="caps">GNOME</span> platform in a myriad of directions. Unlike <span class="caps">GTK</span>, the GObject <span class="caps">API</span> hasn’t seen any major change since its introduction: aside from deprecations and little new functionality, the <span class="caps">API</span> is exactly the same today as it was when GLib 2.0 was released in March 2002. If you transported a <span class="caps">GNOME</span> developer from 2003 to 2023, they would have no problem understanding a newly written GObject class; though, they would likely appreciate the levels of boilerplate reduction, and the performance improvements that have been introduced over the years.</p>
<p>While having a stable <span class="caps">API</span> last this long is definitely a positive, it also imposes a burden on maintainers and users, because any change has to be weighted against the possibility of introducing unintended regressions in code that uses undefined, or undocumented, behaviour. There’s a lot of leeway when it comes to playing games with C, and GObject has dark corners everywhere.</p>
<p>The other burden is that any major change to a foundational library like GObject cascades across the entire platform. Releasing GLib 3.0 today would necessitate breaking <span class="caps">API</span> in the entirety of the <span class="caps">GNOME</span> stack and further beyond; it would require either a hard to execute <a href="https://en.wikipedia.org/wiki/Flag_day_(computing)">“flag day”</a>, or an impossibly long transition, reverberating across downstreams for years to come. Both solutions imply amounts of work that are simply not compatible with a volunteer-based project and ecosystem, especially the current one where volunteers of core components are now stretched thin across too many projects.</p>
<p>And yet, we are now at a cross-roads: our foundational code base has reached the point where recruiting new resources capable of affecting change on the project has become increasingly difficult; where any attempt at performance improvement is heavily counterbalanced by the high possibility of introducing world-breaking regressions; and where fixing the safety and ergonomics of idiomatic code requires unspooling twenty years of limitations inherent to the current design.</p>
<p>Something must be done if we want to improve the coding practices, the performance, and the safety of the platform without a complete rewrite.</p>
<h1>The Mirror</h1>
<blockquote>
<p>‘Many things I can command the Mirror to reveal,’ she answered, ‘and to some I can show what they desire to see. But the Mirror also show things unbidden, and those are often stranger and more profitable than things we wish to behold. What you will see, if you leave the Mirror free to work, I cannot tell. For it shows things that were, and things that are, and things that yet may be. But which it is that he sees, even the wisest cannot always tell. Do you wish to look?’
— Lady Galadriel, “The Lords of the Rings”, Volume 1: The Fellowship of the Ring, Book 2: The Ring Goes South</p>
</blockquote>
<p>In order to properly understand what we want to achieve, we need to understand the problem space that the type system is meant to solve, and the constraints upon which the type system was implemented. We do that by holding GObject up to Galadriel’s Mirror, and gazing into its surface.</p>
<h2>Things that were</h2>
<blockquote>
<p>History became legend. Legend became myth.
— Lady Galadriel, “The Lord of the Rings: The Fellowship of the Ring”</p>
</blockquote>
<p>Before <code>GObject</code> there was <code>GtkObject</code>. It was a simpler time, it was a simpler stack. You added types only for the widgets and objects that related to the <span class="caps">UI</span> toolkit, and everything else was C89, with a touch of undefined behaviour, like calling function pointers with any number of arguments. Properties were “arguments”, likes were florps, and the timeline went sideways.</p>
<p>We had a class initialisation and an instance initialisation functions; properties were stored in a global hash table, but the property multiplexer pair of functions was stored on the type data instead of using the class structure. Types did not have private data: you only had keyed fields. No interfaces, only single inheritance. <code>GtkObject</code> was reference counted, and had an initially “floating” reference, to allow transparent ownership transfer from child to parent container when writing C code, and make the life of every language binding maintainer miserable in the process. There were weak references attached to an instance that worked by invoking a callback when the instance’s reference count reached zero. Signals operated exactly as they do today: large hash table of signal information, indexed by an integer.</p>
<p>None of this was thread safe. After all, <span class="caps">GTK</span> was not thread safe either, because X11 was not thread safe; and we’re talking about 1997: who even had hardware capable of multi-threading at the time? <a href="https://en.wikipedia.org/wiki/Native_POSIX_Thread_Library"><span class="caps">NPTL</span></a> wasn’t even a thing, yet.</p>
<p>The introduction of <code>GObject</code> in 2001 changed <em>some</em> of the rules—mainly, around the idea of having dynamic types that could be loaded and unloaded in order to implement plugins. The basic design of the type system, after all, came from <a href="https://github.com/tim-janik/beast/">Beast</a>, a plugin-heavy audio application, and it was extended to subsume the (mostly) static use cases of <span class="caps">GTK</span>. In order to support unloading, the class aspect of the type system was allowed to be cleaned up, but the type data had to be registered and never unloaded; in other words, once a type was registered, it was there forever.</p>
<p><span class="dquo">“</span>Arguments” were renamed to properties, and were extended to include more than basic types, provide validations, and notify of changes; the overall design was still using a global hash table to store all the properties across all types. Properties were tied to the <code>GObject</code> type, but the property definition existed as a separate type hierarchy that was designed to validate values, but not manage fields inside a class. Signals were ported wholesale, with minimal changes mainly around the marshalling of values and abstracting closures.</p>
<p>The entire plan was to have <code>GObject</code> as <strong>one</strong> of the base classes at the root of a specific hierarchy, with all the required functionality for <span class="caps">GTK</span> to inherit from for its own <code>GtkObject</code>, while leaving open the possibility of creating other hierarchies, or even other roots with different functionality, for more lightweight objects.</p>
<p>These constraints were entirely intentional; the idea was to be able to port <span class="caps">GTK</span> to the new type system, and to an out of tree GLib, during the 1.3 development phase, and minimise the amount of changes necessary to make the transition work not just inside <span class="caps">GTK</span>, but inside of <span class="caps">GNOME</span> too.</p>
<p>Little by little, the entire <code>GObject</code> layer was ported towards thread safety in the only way that worked without breaking the type system: add global locks around everything; use read-write locks for the type data; lock the access and traversal of the property hash table and of the signals table. The only real world code bases that actively exercised multi-threading support were GStreamer and the <span class="caps">GNOME</span> <span class="caps">VFS</span> <span class="caps">API</span> that was mainly used by Nautilus.</p>
<p>With the 3.0 <span class="caps">API</span>, <span class="caps">GTK</span> dropped the <code>GtkObject</code> base type: the whole floating reference mechanism was moved to <code>GObject</code>, and a new type was introduced to provide the “initial” floating reference to derived types. Around the same time, a thread-safe version of weak references for <code>GObject</code> appeared as a separate <span class="caps">API</span>, which confused the matter even more.</p>
<h2>Things that are</h2>
<blockquote>
<p>Darkness crept back into the forests of the world. Rumour grew of a Shadow in the East, whispers of a Nameless Fear.
— Lady Galadriel, “The Lord of the Rings: The Fellowship of the Ring”</p>
</blockquote>
<p>Let’s address the elephant in the room: it’s completely immaterial how many lines of code you have to deal with when creating a new type. It’s a one-off cost, and for most cases, it’s a matter of using the existing macros. The declaration and definition macros have the advantages of enforcing a series of best practices, and keep the code consistent across multiple projects. If you don’t want to deal with boilerplate when using C, you chose the wrong language to begin with. The existence of excessive <span class="caps">API</span> is mainly a requirement to allow other languages to integrate their type system with GObject’s own.</p>
<p>The dynamic part of the type system has gotten progressively less relevant. Yes: you can still create plugins, and those can register types; but classes are never unloaded, just like their type data. There is some attempt at enforcing an order of operations: you cannot just add an interface after a type has been instantiated any more; and while you can add properties and signals after class initialisation, it’s mainly a functionality reserved for specific language bindings to maintain backward compatibility.</p>
<p>Yes, defining properties is boring, and could probably be simplified, but the real cost is not in defining and installing a <code>GParamSpec</code>: it’s in writing the set and get property multiplexers, validating the values, boxing and unboxing the data, and dealing with the different property flags; none of those things can be wrapped in some fancy C preprocessor macros—unless you go into the weeds with <a href="https://en.wikipedia.org/wiki/X_macro">X macros</a>. The other, big cost of properties is their storage inside a separate, global, lock-happy hash table. The main use case of this functionality—adding entirely separate classes of properties with the same semantics as <code>GObject</code> properties, like style properties and child properties in <span class="caps">GTK</span>—has completely fallen out of favour, and for good reasons: it cannot be managed by generic code; it cannot be handled by documentation generators without prior knowledge; and, for the same reason, it cannot be introspected. Even calling these “properties” is kind of a misnomer: they are value validation objects that operate only when using the generic (and less performant) <code>GObject</code> accessor <span class="caps">API</span>, something that is constrained to things like <span class="caps">UI</span> definition files in <span class="caps">GTK</span>, or language bindings. If you use the C accessors for your own <code>GObject</code> type, you’ll have to implement validation yourself; and since idiomatic code will have the generic <code>GObject</code> accessors call the public C <span class="caps">API</span> of your type, you get twice the amount of validation for no reason whatsoever.</p>
<p>Signals have mostly been left alone, outside of performance improvements that were hard to achieve within the constraints of the existing implementation; the generic <span class="caps">FFI</span>-based closure turned out to be a net performance loss, and we’re trying to walk it back even for D-Bus, which was the main driver for it to land in the first place. Marshallers are now generated with a variadic arguments variant, to reduce the amount of boxing and unboxing of <code>GValue</code> containers. Still, there’s not much left to squeeze out of the old GSignal <span class="caps">API</span>.</p>
<p>The atomic nature of the reference counting can be a costly feature, especially for code bases that are by necessity single-threaded; the fact that the reference count field is part of the (somewhat) public <span class="caps">API</span> prevents fundamental refactorings, like switching to biased reference counting for faster operations on the same thread that created an instance. The lack of room on <code>GObject</code> also prevents storing the thread <span class="caps">ID</span> that owns the instance, which in turn prevents calling the <code>GObjectClass.dispose()</code> and <code>GObjectClass.finalize()</code> virtual functions on the right thread, and requires scheduling the destruction of an object on a separate main context, or locking the contents of an object at a further cost.</p>
<h2>Things that yet may be</h2>
<blockquote>
<p>The quest stands upon the edge of a knife: stray but a little, and it will fail to the ruin of all. Yet hope remains, while the company is true.
— Lady Galadriel, “The Lord of the Rings: The Fellowship of the Ring”</p>
</blockquote>
<p>Over the years, we have been strictly focusing on <code>GObject</code>: speeding up its internals, figuring out ways to improve property registration and performance, adding new <span class="caps">API</span> and features to ensure it behaved more reliably. The type system has also been improved, mainly to streamline its use in idiomatic GObject code bases. Not everything worked: <a href="https://gitlab.gnome.org/GNOME/glib/-/issues/1437">properties are still a problem</a>; weak references and pointers are a mess, with two different <span class="caps">API</span> that interact badly with <code>GObject</code>; signals still exists on a completely separate plane; <code>GObject</code> is still wildly inefficient when it comes to locking.</p>
<p>The thesis of this strawman is that we reached the limits of backwards compatibility of <code>GObject</code>, and any attempt at improving it will inevitably lead to a more brittle code, rife with potential regressions. The typical answer, in this case, would be to bump the <span class="caps">API</span>/<span class="caps">ABI</span> of GObject, remove the mistakes of the past, and provide a new idiomatic approach. Sadly, doing so not only would require a level of resources we, as the GLib project stewards, cannot provide, but it would also completely break the entire ecosystem in a way that is not recoverable. Either nobody would port to the new GObject-3.0 <span class="caps">API</span>; or the various projects that depend on GObject would inevitably fracture, following whichever <span class="caps">API</span> version they can commit to; in the meantime, downstream distributors would suffer the worst effects of the shared platform we call “Linux”.</p>
<p>Between inaction and slow death, and action with catastrophic consequences, there’s the possibility of a third option: what if we stopped trying to emulate Java, and have a single <a href="https://en.wikipedia.org/wiki/God_object">“god” type</a>?</p>
<p>Our type system is flexible enough to support partitioning various responsibilities, and we can defer complexity where it belongs: into faster moving dependencies, that have the benefit of being able to iterate and change at a much higher rate than the foundational library of the platform. What’s the point of shoving every possible feature into the base class, in order to cover ever increasingly complex use cases across multiple languages, when we can let consumers decide to opt into their own well-defined behaviours? What GObject ought to provide is a set of reliable types that can be combined in expressive ways, and that can be inspected by generic <span class="caps">API</span>.</p>
<h3>A new, old base type</h3>
<p>We already have a derivable type, called <code>GTypeInstance</code>. Typed instances don’t have any memory management: once instantiated, they can only be moved, or freed. All our objects already are typed instances, since <code>GObject</code> inherits from it. Contrary to <a href="https://www.bassi.io/articles/2020/06/02/type-instances/">the current common practices</a> we should move towards using <code>GTypeInstance</code> for our types.</p>
<p>There’s a distinct lack of convenience <span class="caps">API</span> for defining typed instances, mostly derived from the fact that <code>GTypeInstance</code> is seen as a form of “escape hatch” for projects to use in order to avoid <code>GObject</code>. In practice, there’s nothing that prevents us from improving the convenience of creating new instantiatable/derivable types, especially if we start using them more often. The verbose <span class="caps">API</span> must still exist, to allow language bindings and introspection to handle this kind of types, but just like we made convenience macros for declaring and defining <code>GObject</code> types, we can provide macros for new typed instances, and for setting up a <code>GValue</code> table.</p>
<h3>Optional functionality</h3>
<p>Typed instances require a wrapper <span class="caps">API</span> to free their contents before calling <code>g_type_free_instance()</code>. Nothing prevents us from adding a <code>GFinalizable</code> interface that can be implemented by a <code>GTypeInstance</code>, though: interfaces exist at the type system level, and do not require <code>GObject</code> to work.</p>
<div class="highlight"><pre><span></span><code><span class="k">typedef</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="p">(</span><span class="o">*</span><span class="w"> </span><span class="n">finalize</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">GFinalizable</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="p">);</span>
<span class="p">}</span><span class="w"> </span><span class="n">GFinalizableInterface</span><span class="p">;</span>
</code></pre></div>
<p>If a typed instance provides an implementation of <code>GFinalizable</code>, then <code>g_type_free_instance()</code> can free the contents of the instance by calling <code>g_finalizable_finalize()</code>.</p>
<p>This interface is optional, in case your typed instance just contains simple values, like:</p>
<div class="highlight"><pre><span></span><code><span class="k">typedef</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">GTypeInstance</span><span class="w"> </span><span class="n">parent</span><span class="p">;</span>
<span class="w"> </span><span class="kt">bool</span><span class="w"> </span><span class="n">is_valid</span><span class="p">;</span>
<span class="w"> </span><span class="kt">double</span><span class="w"> </span><span class="n">x1</span><span class="p">,</span><span class="w"> </span><span class="n">y1</span><span class="p">;</span>
<span class="w"> </span><span class="kt">double</span><span class="w"> </span><span class="n">x2</span><span class="p">,</span><span class="w"> </span><span class="n">y2</span><span class="p">;</span>
<span class="p">}</span><span class="w"> </span><span class="n">Box</span><span class="p">;</span>
</code></pre></div>
<p>and does not require deallocations outside of the instance block.</p>
<p>A similar interface can be introduced for cloning instances, allowing a copy operation alongside a move:</p>
<div class="highlight"><pre><span></span><code><span class="k">typedef</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">GClonable</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="p">(</span><span class="o">*</span><span class="w"> </span><span class="n">clone</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">GClonable</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="p">);</span>
<span class="p">}</span><span class="w"> </span><span class="n">GClonable</span><span class="p">;</span>
</code></pre></div>
<p>We could then introduce <code>g_type_instance_clone()</code> as a generic entry point that either used <code>GClonable</code>, or simply allocated a new instance and called <code>memcpy()</code> on it, using the size of the instance (and eventual private data) known to the type system.</p>
<p>The prior art for this kind of functionality exists in <span class="caps">GIO</span>, in the form of the <code>GInitable</code> and <code>GAsyncInitable</code> interfaces; unfortunately, those interfaces require <code>GObject</code>, and they depend on <code>GCancellable</code> and <code>GAsyncResult</code> objects, which prevent us from moving them into the lower level <span class="caps">API</span>.</p>
<h3>Typed containers and life time management</h3>
<p>The main functionality provided by <code>GObject</code> is garbage collection through reference counting: you acquire a (strong) reference when you need to access an instance, and release it when you don’t need the instance any more. If the reference you released was the last one, the instance gets finalized.</p>
<p>Of course, once you introduce strong references you open the door to a veritable bestiary of other type of references:</p>
<ul>
<li><em>weak references</em>, used to keep a “pointer” to the instance, and get a notification when the last reference drops</li>
<li><em>floating references</em>, used as a C convenience to allow ownership transfer of newly constructed “child” objects to their “parent”</li>
<li><em>toggle references</em>, used by language bindings that acquire a strong reference on an instance they wrap with a native object; when the toggle reference gets triggered it means that the last reference being held is the one on the native wrapper, and the wrapper can be dropped causing the instance to be finalized</li>
</ul>
<p>All of these types of reference exist inside <code>GObject</code>, but since they were introduced over the years, they are bolted on top of the base class using the keyed data storage, which comes with its own costly locking and ordering; they are also managed through the finalisation code, which means there are re-entrancy issues or undefined ordering behaviours that routinely crop up over the years, especially when trying to optimise construction and destruction phases.</p>
<p>None of this complexity is, strictly speaking, necessary; we don’t care about an instance being reference counted: a “parent” object can move the memory of a “child” typed instance directly into its own code. What we care about is that, whenever other code interacts with ours, we can hand out a reference to that memory, so that ownership is maintained.</p>
<p>Other languages and standard libraries have the same concept:</p>
<ul>
<li>C++’s <a href="https://en.cppreference.com/w/cpp/memory/shared_ptr"><code>shared_ptr</code></a></li>
<li>Rust’s <a href="https://doc.rust-lang.org/std/rc/index.html"><code>std::rc::Rc</code></a></li>
</ul>
<p>These constructs are not part of a base class: they are wrappers around instances. This means you’re not handing out a reference to an instance: you are handing out a reference to a container, which holds the instance for you. The behaviour of the value is made explicit by the type system, not implicit to the type.</p>
<p>A simple implementation of a typed “reference counted” container would provide us with both strong and weak references:</p>
<div class="highlight"><pre><span></span><code><span class="k">typedef</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">_GRc</span><span class="w"> </span><span class="n">GRc</span><span class="p">;</span>
<span class="k">typedef</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">_GWeak</span><span class="w"> </span><span class="n">GWeak</span><span class="p">;</span>
<span class="n">GRc</span><span class="w"> </span><span class="o">*</span><span class="nf">g_rc_new</span><span class="w"> </span><span class="p">(</span><span class="n">GType</span><span class="w"> </span><span class="n">data_type</span><span class="p">,</span><span class="w"> </span><span class="n">gpointer</span><span class="w"> </span><span class="n">data</span><span class="p">);</span>
<span class="n">GRc</span><span class="w"> </span><span class="o">*</span><span class="nf">g_rc_acquire</span><span class="w"> </span><span class="p">(</span><span class="n">GRc</span><span class="w"> </span><span class="o">*</span><span class="n">rc</span><span class="p">);</span>
<span class="kt">void</span><span class="w"> </span><span class="nf">g_rc_release</span><span class="w"> </span><span class="p">(</span><span class="n">GRc</span><span class="w"> </span><span class="o">*</span><span class="n">rc</span><span class="p">);</span>
<span class="n">gpointer</span><span class="w"> </span><span class="nf">g_rc_get_data</span><span class="w"> </span><span class="p">(</span><span class="n">GRc</span><span class="w"> </span><span class="o">*</span><span class="n">rc</span><span class="p">);</span>
<span class="n">GWeak</span><span class="w"> </span><span class="o">*</span><span class="nf">g_rc_downgrade</span><span class="w"> </span><span class="p">(</span><span class="n">GRc</span><span class="w"> </span><span class="o">*</span><span class="n">rc</span><span class="p">);</span>
<span class="n">GRc</span><span class="w"> </span><span class="o">*</span><span class="nf">g_weak_upgrade</span><span class="w"> </span><span class="p">(</span><span class="n">GWeak</span><span class="w"> </span><span class="o">*</span><span class="n">weak</span><span class="p">);</span>
<span class="kt">bool</span><span class="w"> </span><span class="nf">g_weak_is_empty</span><span class="w"> </span><span class="p">(</span><span class="n">GWeak</span><span class="w"> </span><span class="o">*</span><span class="n">weak</span><span class="p">);</span>
<span class="n">gpointer</span><span class="w"> </span><span class="nf">g_weak_get_data</span><span class="w"> </span><span class="p">(</span><span class="n">GWeak</span><span class="w"> </span><span class="o">*</span><span class="n">weak</span><span class="p">);</span>
</code></pre></div>
<p>Alongside this type of containers, we could also have a specialisation for atomic reference counted containers; or pinned containers, which guarantee that an object is kept in the same memory location; or re-implement referenced containers inside each language binding, to ensure that the behaviour is tailored to the memory management of those languages.</p>
<h3>Specialised types</h3>
<p>Container types introduce the requirement of having the type system understand that an object can be the product of two types: the type of the container, and the type of the data. In order to allow properties, signals, and values to effectively provide introspection of this kind of container types we are going to need to introduce “specialised” types:</p>
<ul>
<li><code>GRc</code> exists as a “generic”, abstract type in the type system</li>
<li>any instance of <code>GRc</code> that contains a instance of type <code>A</code> gets a new type in the type system</li>
</ul>
<p>A basic implementation would look like:</p>
<div class="highlight"><pre><span></span><code><span class="n">GRc</span><span class="w"> </span><span class="o">*</span>
<span class="nf">g_rc_new</span><span class="w"> </span><span class="p">(</span><span class="n">GType</span><span class="w"> </span><span class="n">data_type</span><span class="p">,</span><span class="w"> </span><span class="n">gpointer</span><span class="w"> </span><span class="n">data</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="c1">// Returns an existing GType if something else already</span>
<span class="w"> </span><span class="c1">// has registered the same GRc<T></span>
<span class="w"> </span><span class="n">GType</span><span class="w"> </span><span class="n">rc_type</span><span class="w"> </span><span class="o">=</span>
<span class="w"> </span><span class="n">g_generic_type_register_static</span><span class="w"> </span><span class="p">(</span><span class="n">G_TYPE_RC</span><span class="p">,</span><span class="w"> </span><span class="n">data_type</span><span class="p">);</span>
<span class="w"> </span><span class="c1">// Instantiates GRc, but gives it the type of</span>
<span class="w"> </span><span class="c1">// GRc<T>; there is only the base GRc class</span>
<span class="w"> </span><span class="c1">// and instance initialization functions, as</span>
<span class="w"> </span><span class="c1">// GRc<T> is not a pure derived type</span>
<span class="w"> </span><span class="n">GRc</span><span class="w"> </span><span class="o">*</span><span class="n">res</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="n">GRc</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="w"> </span><span class="n">g_type_create_instance</span><span class="w"> </span><span class="p">(</span><span class="n">rc_type</span><span class="p">);</span>
<span class="w"> </span><span class="n">res</span><span class="o">-></span><span class="n">data</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">data</span><span class="p">;</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">res</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<p>Any instance of type <code>GRc<A></code> satisfies the “is-a” relationship with <code>GRc</code>, but it is not a purely derived type:</p>
<div class="highlight"><pre><span></span><code><span class="n">GType</span><span class="w"> </span><span class="n">rc_type</span><span class="w"> </span><span class="o">=</span>
<span class="w"> </span><span class="p">((</span><span class="n">GTypeInstance</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="w"> </span><span class="n">rc</span><span class="p">)</span><span class="o">-></span><span class="n">g_class</span><span class="p">.</span><span class="n">g_type</span><span class="p">;</span>
<span class="n">g_assert_true</span><span class="w"> </span><span class="p">(</span><span class="n">g_type_is_a</span><span class="w"> </span><span class="p">(</span><span class="n">rc_type</span><span class="p">,</span><span class="w"> </span><span class="n">G_TYPE_RC</span><span class="p">));</span>
</code></pre></div>
<p>The <code>GRc<A></code> type does not have a different instance or class size, or its own class and instance initialisation functions; it’s still an instance of the <code>GRc</code> type, with a different <code>GType</code>. The <code>GRc<A></code> type only exists at run time, as it is the result of the type instantiation; you <strong>cannot</strong> instantiate a plain <code>GRc</code>, or derive your type from <code>GRc</code> in order to create your own reference counted type, either:</p>
<div class="highlight"><pre><span></span><code><span class="c1">// WRONG</span>
<span class="n">GRc</span><span class="w"> </span><span class="o">*</span><span class="n">rc</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">g_type_create_instance</span><span class="w"> </span><span class="p">(</span><span class="n">G_TYPE_RC</span><span class="p">);</span>
<span class="c1">// WRONG</span>
<span class="k">typedef</span><span class="w"> </span><span class="n">GRc</span><span class="w"> </span><span class="n">GtkWidget</span><span class="p">;</span>
</code></pre></div>
<p>You can only use a <code>GRc</code> inside your own instance:</p>
<div class="highlight"><pre><span></span><code><span class="k">typedef</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="c1">// GRc<GtkWidget></span>
<span class="w"> </span><span class="n">GRc</span><span class="w"> </span><span class="o">*</span><span class="n">parent</span><span class="p">;</span>
<span class="w"> </span><span class="c1">// GRc<GtkWidget></span>
<span class="w"> </span><span class="n">GRc</span><span class="w"> </span><span class="o">*</span><span class="n">first_child</span><span class="p">;</span>
<span class="w"> </span><span class="c1">// GRc<GtkWidget></span>
<span class="w"> </span><span class="n">GRc</span><span class="w"> </span><span class="o">*</span><span class="n">next_sibling</span><span class="p">;</span>
<span class="w"> </span><span class="c1">// ...</span>
<span class="p">}</span><span class="w"> </span><span class="n">GtkWidgetPrivate</span><span class="p">;</span>
</code></pre></div>
<h3>Tuple types</h3>
<p>Tuples are generic containers of N values, but right now we don’t have any way of formally declaring them into the type system. A hack is to use arrays of similarly typed values, but with the deprecation of <code>GValueArray</code>—which is a bad type that does not allow reference counting, and does not give you guarantees anyway—we only have C arrays and pointer types.</p>
<p>Registering a new tuple type would work like a generic type: a base <code>GTuple</code> abstract type as the “parent”, and a number of types:</p>
<div class="highlight"><pre><span></span><code><span class="k">typedef</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">_GTuple</span><span class="w"> </span><span class="n">GTuple</span><span class="p">;</span>
<span class="n">GTuple</span><span class="w"> </span><span class="o">*</span>
<span class="nf">g_tuple_new_int</span><span class="w"> </span><span class="p">(</span><span class="kt">size_t</span><span class="w"> </span><span class="n">n_elements</span><span class="p">,</span>
<span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">elements</span><span class="p">[])</span>
<span class="p">{</span>
<span class="w"> </span><span class="n">GType</span><span class="w"> </span><span class="n">tuple_type</span><span class="w"> </span><span class="o">=</span>
<span class="w"> </span><span class="n">g_tuple_type_register_static</span><span class="w"> </span><span class="p">(</span><span class="n">G_TYPE_TUPLE</span><span class="p">,</span><span class="w"> </span><span class="n">n_elements</span><span class="p">,</span><span class="w"> </span><span class="n">G_TYPE_INT</span><span class="p">);</span>
<span class="w"> </span><span class="n">GTuple</span><span class="w"> </span><span class="o">*</span><span class="n">res</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">g_type_create_instance</span><span class="w"> </span><span class="p">(</span><span class="n">tuple_type</span><span class="p">);</span>
<span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kt">size_t</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o"><</span><span class="w"> </span><span class="n">n_elements</span><span class="p">;</span><span class="w"> </span><span class="n">i</span><span class="o">++</span><span class="p">)</span>
<span class="w"> </span><span class="n">g_tuple_add</span><span class="w"> </span><span class="p">(</span><span class="n">res</span><span class="p">,</span><span class="w"> </span><span class="n">elements</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">res</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<p>We can also create specialised tuple types, like pairs:</p>
<div class="highlight"><pre><span></span><code><span class="k">typedef</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">_GPair</span><span class="w"> </span><span class="n">GPair</span><span class="p">;</span>
<span class="n">GPair</span><span class="w"> </span><span class="o">*</span>
<span class="nf">g_pair_new</span><span class="w"> </span><span class="p">(</span><span class="n">GType</span><span class="w"> </span><span class="n">this_type</span><span class="p">,</span>
<span class="w"> </span><span class="n">GType</span><span class="w"> </span><span class="n">that_type</span><span class="p">,</span>
<span class="w"> </span><span class="p">...);</span>
</code></pre></div>
<p>This would give use the ability to standardise our <span class="caps">API</span> around fundamental types, and reduce the amount of <em>ad hoc</em> container types that libraries have to define and bindings have to wrap with native constructs.</p>
<h3>Sum types</h3>
<p>Of course, once we start with specialised types, we end up with sum types:</p>
<div class="highlight"><pre><span></span><code><span class="k">typedef</span><span class="w"> </span><span class="k">enum</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">SQUARE</span><span class="p">,</span>
<span class="w"> </span><span class="n">RECT</span><span class="p">,</span>
<span class="w"> </span><span class="n">CIRCLE</span><span class="p">,</span>
<span class="p">}</span><span class="w"> </span><span class="n">ShapeKind</span><span class="p">;</span>
<span class="k">typedef</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">GTypeInstance</span><span class="w"> </span><span class="n">parent</span><span class="p">;</span>
<span class="w"> </span><span class="n">ShapeKind</span><span class="w"> </span><span class="n">kind</span><span class="p">;</span>
<span class="w"> </span><span class="k">union</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">Point</span><span class="w"> </span><span class="n">origin</span><span class="p">;</span><span class="w"> </span><span class="kt">float</span><span class="w"> </span><span class="n">side</span><span class="p">;</span><span class="w"> </span><span class="p">};</span>
<span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">Point</span><span class="w"> </span><span class="n">origin</span><span class="p">;</span><span class="w"> </span><span class="n">Size</span><span class="w"> </span><span class="n">size</span><span class="p">;</span><span class="w"> </span><span class="p">};</span>
<span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">Point</span><span class="w"> </span><span class="n">center</span><span class="p">;</span><span class="w"> </span><span class="kt">float</span><span class="w"> </span><span class="n">radius</span><span class="p">;</span><span class="w"> </span><span class="p">};</span>
<span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="n">shape</span><span class="p">;</span>
<span class="p">}</span><span class="w"> </span><span class="n">Shape</span><span class="p">;</span>
</code></pre></div>
<p>As of right now, discriminated unions don’t have any special handling in the type system: they are generally boxed types, or typed instances, but they require type-specific <span class="caps">API</span> to deal with the discriminator field and type. Since we have types for enumerations and instances, we can register them at the same time, and provide offsets for direct access:</p>
<div class="highlight"><pre><span></span><code><span class="n">GType</span>
<span class="nf">g_sum_type_register_static</span><span class="w"> </span><span class="p">(</span><span class="k">const</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">name</span><span class="p">,</span>
<span class="w"> </span><span class="kt">size_t</span><span class="w"> </span><span class="n">class_size</span><span class="p">,</span>
<span class="w"> </span><span class="kt">size_t</span><span class="w"> </span><span class="n">instance_size</span><span class="p">,</span>
<span class="w"> </span><span class="n">GType</span><span class="w"> </span><span class="n">tag_enum_type</span><span class="p">,</span>
<span class="w"> </span><span class="n">offset_t</span><span class="w"> </span><span class="n">tag_field</span><span class="p">);</span>
</code></pre></div>
<p>This way it’s possible to ask the type system for:</p>
<ul>
<li>the offset of the tag in an instance, for direct access</li>
<li>all the possible values of the tag, by inspecting its <code>GEnum</code> type</li>
</ul>
<p>From then on, we can easily build types like <code>Option</code> and <code>Result</code>:</p>
<div class="highlight"><pre><span></span><code><span class="k">typedef</span><span class="w"> </span><span class="k">enum</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">G_RESULT_OK</span><span class="p">,</span>
<span class="w"> </span><span class="n">G_RESULT_ERR</span>
<span class="p">}</span><span class="w"> </span><span class="n">GResultKind</span><span class="p">;</span>
<span class="k">typedef</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">GTypeInstance</span><span class="w"> </span><span class="n">parent</span><span class="p">;</span>
<span class="w"> </span><span class="n">GResultKind</span><span class="w"> </span><span class="n">type</span><span class="p">;</span>
<span class="w"> </span><span class="k">union</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">GValue</span><span class="w"> </span><span class="n">value</span><span class="p">;</span>
<span class="w"> </span><span class="n">GError</span><span class="w"> </span><span class="o">*</span><span class="n">error</span><span class="p">;</span>
<span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="n">result</span><span class="p">;</span>
<span class="p">}</span><span class="w"> </span><span class="n">GResult</span><span class="p">;</span>
<span class="c1">// ...</span>
<span class="n">g_sum_type_register_static</span><span class="w"> </span><span class="p">(</span><span class="s">"GResult"</span><span class="p">,</span>
<span class="w"> </span><span class="k">sizeof</span><span class="w"> </span><span class="p">(</span><span class="n">GResultClass</span><span class="p">),</span>
<span class="w"> </span><span class="k">sizeof</span><span class="w"> </span><span class="p">(</span><span class="n">GResult</span><span class="p">),</span>
<span class="w"> </span><span class="n">G_TYPE_RESULT_KIND</span><span class="p">,</span>
<span class="w"> </span><span class="n">offsetof</span><span class="w"> </span><span class="p">(</span><span class="n">GResult</span><span class="p">,</span><span class="w"> </span><span class="n">type</span><span class="p">));</span>
<span class="c1">// ...</span>
<span class="n">GResult</span><span class="w"> </span><span class="o">*</span>
<span class="nf">g_result_new_boolean</span><span class="w"> </span><span class="p">(</span><span class="n">gboolean</span><span class="w"> </span><span class="n">value</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="n">GType</span><span class="w"> </span><span class="n">res_type</span><span class="w"> </span><span class="o">=</span>
<span class="w"> </span><span class="n">g_generic_type_register_static</span><span class="w"> </span><span class="p">(</span><span class="n">G_TYPE_RESULT</span><span class="p">,</span>
<span class="w"> </span><span class="n">G_TYPE_BOOLEAN</span><span class="p">)</span>
<span class="w"> </span><span class="n">GResult</span><span class="w"> </span><span class="o">*</span><span class="n">res</span><span class="w"> </span><span class="o">=</span>
<span class="w"> </span><span class="n">g_type_create_instance</span><span class="w"> </span><span class="p">(</span><span class="n">res_type</span><span class="p">);</span>
<span class="w"> </span><span class="n">g_value_set_boolean</span><span class="w"> </span><span class="p">(</span><span class="o">&</span><span class="n">res</span><span class="o">-></span><span class="n">result</span><span class="p">.</span><span class="n">value</span><span class="p">,</span><span class="w"> </span><span class="n">value</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">res</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// ...</span>
<span class="n">g_autoptr</span><span class="w"> </span><span class="p">(</span><span class="n">GResult</span><span class="p">)</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">obj_finish</span><span class="w"> </span><span class="p">(</span><span class="n">task</span><span class="p">);</span>
<span class="k">switch</span><span class="w"> </span><span class="p">(</span><span class="n">g_result_get_kind</span><span class="w"> </span><span class="p">(</span><span class="n">result</span><span class="p">))</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="no">G_RESULT_OK</span><span class="p">:</span>
<span class="w"> </span><span class="n">g_print</span><span class="w"> </span><span class="p">(</span><span class="s">"Result: %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span>
<span class="w"> </span><span class="n">g_result_get_boolean</span><span class="w"> </span><span class="p">(</span><span class="n">result</span><span class="p">)</span>
<span class="w"> </span><span class="o">?</span><span class="w"> </span><span class="s">"true"</span>
<span class="w"> </span><span class="o">:</span><span class="w"> </span><span class="s">"false"</span><span class="p">);</span>
<span class="w"> </span><span class="k">break</span><span class="p">;</span>
<span class="w"> </span><span class="k">case</span><span class="w"> </span><span class="no">G_RESULT_ERROR</span><span class="p">:</span>
<span class="w"> </span><span class="n">g_printerr</span><span class="w"> </span><span class="p">(</span><span class="s">"Error: %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span>
<span class="w"> </span><span class="n">g_result_get_error_message</span><span class="w"> </span><span class="p">(</span><span class="n">result</span><span class="p">));</span>
<span class="w"> </span><span class="k">break</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// ...</span>
<span class="n">g_autoptr</span><span class="w"> </span><span class="p">(</span><span class="n">GResult</span><span class="p">)</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="o">=</span>
<span class="w"> </span><span class="n">g_input_stream_read_bytes</span><span class="w"> </span><span class="p">(</span><span class="n">stream</span><span class="p">);</span>
<span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">g_result_is_error</span><span class="w"> </span><span class="p">(</span><span class="n">result</span><span class="p">))</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="c1">// ...</span>
<span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">g_autoptr</span><span class="w"> </span><span class="p">(</span><span class="n">GBytes</span><span class="p">)</span><span class="w"> </span><span class="n">data</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">g_result_get_boxed</span><span class="w"> </span><span class="p">(</span><span class="n">result</span><span class="p">);</span>
<span class="w"> </span><span class="c1">// ...</span>
<span class="p">}</span>
</code></pre></div>
<h3>Consolidating GLib and GType</h3>
<p>Having the type system in a separate shared library did make sense back when GLib was spun off from <span class="caps">GTK</span>; after all, GLib was mainly a set of convenient data types for a language that lacked a decent standard library. Additionally, not many C projects were interested in the type system, as it was perceived as a big chunk of functionality in an era where space was at a premium. These days, the smallest environment capable of running GLib code is plenty capable of running the GObject type system as well. The separation between GLib data types and the GObject type system has created data types that are not type safe, and work by copying data, by having run time defined destructor functions, or by storing pointers and assuming everything will be fine. This leads to code duplication between shared libraries, and prevents the use of GLib data types in the public <span class="caps">API</span>, lest the introspection information gets lost.</p>
<p>Moving the type system inside GLib would allow us to have properly typed generic container types, like a <code>GVector</code> replacing <code>GArray</code>, <code>GPtrArray</code>, <code>GByteArray</code>, as well as the deprecated <code>GValueArray</code>; or a <code>GMap</code> and a <code>GSet</code>, replacing <code>GHashTable</code>, <code>GSequence</code>, and <code>GtkRBTree</code>. Even the various list models could be assembled on top of these new types, and moved out of <span class="caps">GTK</span>.</p>
<p>Current consumers of GLib-only <span class="caps">API</span> would still have their basic C types, but if they don’t want to link against a slightly bigger shared library that includes <code>GTypeInstance</code>, <code>GTypeInterface</code>, and the newly added generic, tuple, and sum types, then they would probably be better served by projects like <a href="https://github.com/c-util">c-util</a> instead.</p>
<h3>Properties</h3>
<p>Instead of bolting properties on top of <code>GParamSpec</code>, we can move their definition into the type system; after all, properties are a fundamental part of a type, so it does not make sense to bind them to the class instantiation. This would also remove the long-standing issue of properties being available for registration long after a class has been initialised; it would give us the chance to ship a utility for inspecting the type system to get all the meta-information on the hierarchy and generating introspection <span class="caps">XML</span> without having to compile a small binary.</p>
<p>If we move property registration to the type registration we can also finally move away from multiplexed accessors, and use direct instance field access where applicable:</p>
<div class="highlight"><pre><span></span><code><span class="n">GPropertyBuilder</span><span class="w"> </span><span class="n">builder</span><span class="p">;</span>
<span class="n">g_property_builder_init</span><span class="w"> </span><span class="p">(</span><span class="o">&</span><span class="n">builder</span><span class="p">,</span>
<span class="w"> </span><span class="n">G_TYPE_STRING</span><span class="p">,</span><span class="w"> </span><span class="s">"name"</span><span class="p">);</span>
<span class="c1">// Stop using flags, and use proper setters; since</span>
<span class="c1">// there's no use case for unsetting the readability</span>
<span class="c1">// flag, we don't even need a boolean argument</span>
<span class="n">g_property_builder_set_readwrite</span><span class="w"> </span><span class="p">(</span><span class="o">&</span><span class="n">builder</span><span class="p">);</span>
<span class="c1">// The offset is used for read and write access...</span>
<span class="n">g_property_builder_set_private_offset</span><span class="w"> </span><span class="p">(</span><span class="o">&</span><span class="n">builder</span><span class="p">,</span>
<span class="w"> </span><span class="n">offsetof</span><span class="w"> </span><span class="p">(</span><span class="n">GtkWidgetPrivate</span><span class="p">,</span><span class="w"> </span><span class="n">name</span><span class="p">));</span>
<span class="c1">// ... unless an accessor function is provided; in</span>
<span class="c1">// this case we want setting a property to go through</span>
<span class="c1">// a function</span>
<span class="n">g_property_builder_set_setter_func</span><span class="w"> </span><span class="p">(</span><span class="o">&</span><span class="n">builder</span><span class="p">,</span>
<span class="w"> </span><span class="n">gtk_widget_set_name</span><span class="p">);</span>
<span class="c1">// Register the property into the type; we return the</span>
<span class="c1">// offset of the property into the type node, so we can</span>
<span class="c1">// access the property definition with a fast look up</span>
<span class="n">properties</span><span class="p">[</span><span class="n">NAME</span><span class="p">]</span><span class="w"> </span><span class="o">=</span>
<span class="w"> </span><span class="n">g_type_add_instance_property</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="p">,</span>
<span class="w"> </span><span class="n">g_property_builder_end</span><span class="w"> </span><span class="p">(</span><span class="o">&</span><span class="n">builder</span><span class="p">));</span>
</code></pre></div>
<p>Accessing the property information would then be a case of looking into the type system under a single reader lock, instead of traversing all properties in a glorified globally locked hash table.</p>
<p>Once we have a property registered in the type system, accessing it is a matter of calling <span class="caps">API</span> on the <code>GProperty</code> object:</p>
<div class="highlight"><pre><span></span><code><span class="kt">void</span>
<span class="nf">gtk_widget_set_name</span><span class="w"> </span><span class="p">(</span><span class="n">GtkWidget</span><span class="w"> </span><span class="o">*</span><span class="n">widget</span><span class="p">,</span>
<span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">name</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="n">GProperty</span><span class="w"> </span><span class="o">*</span><span class="n">prop</span><span class="w"> </span><span class="o">=</span>
<span class="w"> </span><span class="n">g_type_get_instance_property</span><span class="w"> </span><span class="p">(</span><span class="n">GTK_TYPE_WIDGET</span><span class="p">,</span>
<span class="w"> </span><span class="n">properties</span><span class="p">[</span><span class="n">NAME</span><span class="p">]);</span>
<span class="w"> </span><span class="n">g_property_set</span><span class="w"> </span><span class="p">(</span><span class="n">prop</span><span class="p">,</span><span class="w"> </span><span class="n">name</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div>
<h3>Signals</h3>
<p>Moving signal registration into the type system would allow us to subsume the global locking into the type locks; it would also give us the chance to simplify some of the complexity for re-emission and hooks:</p>
<div class="highlight"><pre><span></span><code><span class="n">GSignalBuilder</span><span class="w"> </span><span class="n">builder</span><span class="p">;</span>
<span class="n">g_signal_builder_init</span><span class="w"> </span><span class="p">(</span><span class="o">&</span><span class="n">builder</span><span class="p">,</span><span class="w"> </span><span class="s">"insert-text"</span><span class="p">);</span>
<span class="n">g_signal_builder_set_args</span><span class="w"> </span><span class="p">(</span><span class="o">&</span><span class="n">builder</span><span class="p">,</span><span class="w"> </span><span class="mi">3</span><span class="p">,</span>
<span class="w"> </span><span class="p">(</span><span class="n">GSignalArg</span><span class="p">[])</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">.</span><span class="n">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">"text"</span><span class="p">,</span><span class="w"> </span><span class="p">.</span><span class="n">gtype</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">G_TYPE_STRING</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">.</span><span class="n">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">"length"</span><span class="p">,</span><span class="w"> </span><span class="p">.</span><span class="n">gtype</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">G_TYPE_SIZE</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">.</span><span class="n">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">"position"</span><span class="p">,</span><span class="w"> </span><span class="p">.</span><span class="n">gtype</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">G_TYPE_OFFSET</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">});</span>
<span class="n">g_signal_builder_set_retval</span><span class="w"> </span><span class="p">(</span><span class="o">&</span><span class="n">builder</span><span class="p">,</span>
<span class="w"> </span><span class="n">G_TYPE_OFFSET</span><span class="p">);</span>
<span class="n">g_signal_builder_set_class_offset</span><span class="w"> </span><span class="p">(</span><span class="o">&</span><span class="n">builder</span><span class="p">,</span>
<span class="w"> </span><span class="n">offsetof</span><span class="w"> </span><span class="p">(</span><span class="n">EditableClass</span><span class="p">,</span><span class="w"> </span><span class="n">insert_text</span><span class="p">));</span>
<span class="n">signals</span><span class="p">[</span><span class="n">INSERT_TEXT</span><span class="p">]</span><span class="w"> </span><span class="o">=</span>
<span class="w"> </span><span class="n">g_type_add_class_signal</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="p">,</span>
<span class="w"> </span><span class="n">g_signal_builder_end</span><span class="w"> </span><span class="p">(</span><span class="o">&</span><span class="n">builder</span><span class="p">));</span>
</code></pre></div>
<p>By taking the chance of moving signals out of the their own namespace we can also move to a model where each class is responsible for providing the <span class="caps">API</span> necessary to connect and emit signals, as well as providing callback types for each signal. This would allow us to increase type safety, and reduce the reliance on generic <span class="caps">API</span>:</p>
<div class="highlight"><pre><span></span><code><span class="k">typedef</span><span class="w"> </span><span class="n">offset_t</span><span class="w"> </span><span class="p">(</span><span class="o">*</span><span class="w"> </span><span class="n">EditableInsertText</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Editable</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="p">,</span>
<span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">text</span><span class="p">,</span>
<span class="w"> </span><span class="kt">size_t</span><span class="w"> </span><span class="n">length</span><span class="p">,</span>
<span class="w"> </span><span class="n">offset_t</span><span class="w"> </span><span class="n">position</span><span class="p">);</span>
<span class="kt">unsigned</span><span class="w"> </span><span class="kt">long</span>
<span class="nf">editable_connect_insert_text</span><span class="w"> </span><span class="p">(</span><span class="n">Editable</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="p">,</span>
<span class="w"> </span><span class="n">EditableInsertText</span><span class="w"> </span><span class="n">callback</span><span class="p">,</span>
<span class="w"> </span><span class="n">gpointer</span><span class="w"> </span><span class="n">user_data</span><span class="p">,</span>
<span class="w"> </span><span class="n">GSignalFlags</span><span class="w"> </span><span class="n">flags</span><span class="p">);</span>
<span class="n">offset_t</span>
<span class="nf">editable_emit_insert_text</span><span class="w"> </span><span class="p">(</span><span class="n">Editable</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="p">,</span>
<span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">text</span><span class="p">,</span>
<span class="w"> </span><span class="kt">size_t</span><span class="w"> </span><span class="n">length</span><span class="p">,</span>
<span class="w"> </span><span class="n">offset_t</span><span class="w"> </span><span class="n">position</span><span class="p">);</span>
</code></pre></div>
<h3>Extending the type system</h3>
<p>Some of the metadata necessary to provide properly typed properties and signals is missing from the type system. For instance, by design, there is no type representing a <code>uint16_t</code>; we are supposed to create a <code>GParamSpec</code> to validate the value of a <code>G_TYPE_INT</code> in order to fit in the 16bit range. Of course, this leads to excessive run time validation, and relies on C’s own promotion rules for variadic arguments; it also does not work for signals, as those do not use <code>GParamSpec</code>. More importantly, though, the missing connection between C types and GTypes prevents gathering proper introspection information for properties and signal arguments: if we only have the GType we cannot generate the full metadata that can be used by documentation and language bindings, unless we’re willing to lose specificity.</p>
<p>Not only the type system should be sufficient to contain all the standard C types that are now available, we also need the type system to provide us with enough information to be able to serialise those types into the introspection data, if we want to be able to generate code like signal <span class="caps">API</span>, type safe bindings, or accurate documentation for properties and signal handlers.</p>
<h3>Introspection</h3>
<p>Introspection exists outside of GObject mainly because of dependencies; the parser, abstract syntax tree, and transformers are written in Python and interface with a low level C tokeniser. Adding a CPython dependency to GObject is too much of a stretch, especially when it comes to bootstrapping a system. While we could keep the dependency optional, and allow building GObject without support for introspection, keeping the code separate is a simpler solution.</p>
<p>Nevertheless, GObject should not ignore introspection. The current reflection <span class="caps">API</span> inside GObject should generate data that is compatible with the libgirepository <span class="caps">API</span> and with its <span class="caps">GIR</span> parser. Currently, gobject-introspection is tasked with generating a small C executable, compiling it, running it to extract metadata from the type system, as well as the properties and signals of a <code>GObject</code> type, and generate <span class="caps">XML</span> that can be parsed and included into the larger <span class="caps">GIR</span> metadata for the rest of the <span class="caps">ABI</span> being introspected. GObject should ship a pre-built binary, instead; it should <code>dlopen</code> the given library or executable, extract all the type information, and emit the introspection data. This would not make gobject-introspection more cross-compilable, but it would simplify its internals and its distributability. We would not need to know how to compile and run C code from a Python script, for one; a simple executable wrapper around a native copy of the GObject-provided binary would be enough.</p>
<p>Ideally, we could move the girepository <span class="caps">API</span> into GObject itself, and allow it to load the binary data compiled out of the <span class="caps">XML</span>; language bindings loading the data at run time would then need to depend on GObject instead of an additional library, and we could ship the <span class="caps">GIR</span> → typelib compiler directly with GLib, leaving gobject-introspection to deal only with the parsing of C headers, docblocks, and annotations, to generate the <span class="caps">XML</span> representation of the C/GObject <span class="caps">ABI</span>.</p>
<h2>There and back again</h2>
<blockquote>
<p>And the ship went out into the High Sea and passed on into the West, until at last on a night of rain Frodo smelled a sweet fragrance on the air and heard the sound of singing that came over the water. And then it seemed to him that as in his dream in the house of Bombadil, the grey rain-curtain turned all to silver glass and was rolled back, and he beheld white shores and beyond them a far green country under a swift sunrise.
— “The Lord of the Rings”, Volume 3: The Return of the King, Book 6: The End of the Third Age</p>
</blockquote>
<p>The hard part of changing a project in a backward compatible way is resisting the temptation of fixing the existing design. Some times it’s necessary to backtrack the chain of decisions, and consider the extant code base a dead branch; not because the code is wrong, or bug free, but because any attempt at doubling down on the same design will inevitably lead to breakage. In this sense, it’s easy to just declare “maintenance bankruptcy”, and start from a new major <span class="caps">API</span> version: breaks allow us to fix the implementation, at the cost of adapting to new <span class="caps">API</span>. For instance, widgets are still the core of <span class="caps">GTK</span>, even after 4 major revisions; we did not rename them to “elements” or “actors”, and we did not change how the windows are structured. You are still supposed to build a tree of widgets, connect callbacks to signals, and let the main event loop run. Porting has been painful because of underlying changes in the graphics stack, or because of portability concerns, but even with the direction change of favouring composition over inheritance, the knowledge on how to use <span class="caps">GTK</span> has been transferred from <span class="caps">GTK</span> 1 to 4.</p>
<p>We cannot do the same for <code>GObject</code>. Changing how it is implemented implies changing everything that depends on it; it means introducing behavioural changes in subtle, and hard to predict ways. Luckily for us, the underlying type system is still flexible and nimble enough that it can give us the ability to change direction, and implement an entirely different approach to object orientation—one that is more in line with languages like modern C++ and Rust. By following new approaches we can slowly migrate our platform to other languages over time, with a smaller impedance mismatch caused by the current design of our object model. Additionally, by keeping the root of the type system, we maintain the ability to provide a stable C <span class="caps">ABI</span> that can be consumed by multiple languages, which is the strong selling point of the <span class="caps">GNOME</span> ecosystem.</p>
<p>Why do all of this work, though? Compared to a full <span class="caps">API</span> break, this proposal has the advantage of being tractable and realistic; I cannot overemphasise enough how little appetite there is for a “GObject 3.0” in the ecosystem. The recent <span class="caps">API</span> bump from libsoup2 to libsoup3 has clearly identified that changes deep into the stack end up being too costly an effort: some projects have found it easier to switch to another <span class="caps">HTTP</span> library altogether, rather than support two versions of libsoup for a while; other projects have decided to drop compatibility with libsoup2, forcing the hand of every reverse dependency both upstream and downstream. Breaking GObject would end up breaking the ecosystem, with the hope of a “perfect” implementation way down the line and with very few users on one side, and a dead branch used by everybody else on the other.</p>
<p>Of course, the complexity of the change is not going to be trivial, and it will impact things like the introspection metadata and the various language bindings that exist today; some bindings may even require a complete redesign. Nevertheless, by implementing this new object model and leaving <code>GObject</code> alone, we buy ourselves enough time and space to port our software development platform towards a different future.</p>
<p>Maybe this way we will get to save the Shire; and even if we give up some things, or even lose them, we still get to keep what matters.</p>Configuring portals2023-05-29T18:31:00+01:002023-05-30T14:59:41+01:00ebassitag:www.bassi.io,2023-05-29:/articles/2023/05/29/configuring-portals/<p>In which I explain what desktop developers, distributions, and users can do to configure sandbox portals</p><p>One of the things I’ve been recently working on at <a href="https://igalia.com">Igalia</a> is the
desktop portals implementation, the middleware layer of <span class="caps">API</span> for application
and toolkit developers that allows sandboxed applications to interact with
the host system. Sandboxing technologies like <a href="https://docs.flatpak.org/en/latest/desktop-integration.html#portals">Flatpak</a> and
<a href="https://snapcraft.io/docs/xdg-desktop-portals">Snap</a> expose the portal D-Bus interfaces inside the sandbox
they manage, to handle user-mediated interactions like opening a file that
exists outside of the locations available to the sandboxed process, or
talking to privileged components like the compositor to obtain a screenshot.</p>
<p>Outside of allowing dynamic permissions for sandboxed applications, portals
act as a vendor-neutral <span class="caps">API</span> for applications to target when dealing with
Linux as an <span class="caps">OS</span>; this is mostly helpful for commercial applications that are
not tied to a specific desktop environment, but don’t want to re-implement
the layer of system integration from the first principles of <span class="caps">POSIX</span> primitives.</p>
<p>The architecture of desktop portals has been described pretty well in <a href="http://who-t.blogspot.com/2021/08/flatpak-portals-how-do-they-work.html">a
blog post by Peter Hutterer</a>, but to recap:</p>
<ul>
<li>desktop portals are <a href="https://flatpak.github.io/xdg-desktop-portal/">a series of D-Bus interfaces</a></li>
<li>toolkits and applications call methods on those D-Bus interfaces</li>
<li>there is a user session daemon called
<a href="https://github.com/flatpak/xdg-desktop-portal">xdg-desktop-portal</a> that provides a
service for the D-Bus interfaces</li>
<li>xdg-desktop-portal implements some of those interface directly</li>
<li>for the interfaces that involve user interaction, or interaction with
desktop-specific services, we have separate services that are proxied
by xdg-desktop-portal; <span class="caps">GNOME</span> has <a href="https://gitlab.gnome.org/GNOME/xdg-desktop-portal-gnome">xdg-desktop-portal-gnome</a>,
<span class="caps">KDE</span> has <a href="https://invent.kde.org/plasma/xdg-desktop-portal-kde">xdg-desktop-portal-kde</a>; Sway and wlroot-based
compositors have <a href="https://github.com/emersion/xdg-desktop-portal-wlr">xdg-desktop-portal-wlr</a>; and so on, and so forth</li>
</ul>
<p>There’s also <a href="https://github.com/flatpak/xdg-desktop-portal-gtk">xdg-desktop-portal-gtk</a>, which acts a bit as a
reference portal implementation, and a shared desktop portal implementation
for a lot of <span class="caps">GTK</span>-based environments. Ideally, every desktop environment
should have their own desktop portal implementation, so that applications
using the portal <span class="caps">API</span> can be fully integrated with each desktop’s interface
guidelines and specialised services.</p>
<p>One thing that is currently messy is the mechanism by which
xdg-desktop-portal finds the portal implementations available on the system,
and decides which implementation should be used for a specific interface.</p>
<p>Up until the current stable version of xdg-desktop-portal, the configuration
worked this way:</p>
<ol>
<li>each portal implementation (xdg-desktop-portal-gtk, -gnome, -kde, …)
ships a <code>${NAME}.portal</code> file; the file is a simple <span class="caps">INI</span>-like desktop
entry file with the following keys:<ul>
<li><code>DBusName</code>, which contains the service name of the portal, for
instance, <code>org.freedesktop.impl.portal.desktop.gnome</code> for the <span class="caps">GNOME</span>
portals; this name is used by xdg-desktop-portal to launch the portal implementation</li>
<li><code>Interfaces</code>, which contains a list of D-Bus interfaces under the
<code>org.freedesktop.impl.portal.*</code> namespace that are implemented by the
desktop-specific portal; xdg-desktop-portal will match the portal
implementation with the public facing D-Bus interface internally</li>
<li><code>UseIn</code>, which contains the name of the desktop to be matched with the
contents of the <code>$XDG_CURRENT_DESKTOP</code> environment variable</li>
</ul>
</li>
<li>once xdg-desktop-portal starts, it finds all the <code>.portal</code> files in a
well-known location and builds a list of portal implementations currently
installed in the system, containing all the interfaces they implement as
well as their preferred desktop environment</li>
<li>whenever something calls a method on an interface in the
<code>org.freedesktop.portal.*</code> namespace, xdg-desktop-portal will check the
current desktop using the <code>XDG_CURRENT_DESKTOP</code> environment variable, and
check if the portal that has a <code>UseIn</code> key that matches the current desktop</li>
<li>once there’s a match, xdg-desktop-portal will activate the portal
implementation and proxy the calls made on the <code>org.freedesktop.portal</code>
interfaces over to the <code>org.freedesktop.impl.portal</code> ones</li>
</ol>
<p>This works perfectly fine for the average case of a Linux installation with
a single session, using a single desktop environment, and a single desktop
portal. Where things get messy is the case where you have multiple sessions
on the same system, each with its own desktop and portals, or even no
portals whatsoever. In a bad scenario, you may get the wrong desktop portal
just because the name sorts before the one you’re interested in, so you get
the <span class="caps">GTK</span> “reference” portals instead of the <span class="caps">KDE</span>-specific ones; in the
worst case scenario, you may get a stall when launching an application just
because the wrong desktop portal is trying to contact a session service that
simply does not exist, and you have to wait 30 seconds for a D-Bus timeout.</p>
<p>The problem is that some desktop portal implementations are shared across
desktops, or cover only a limited amount of interfaces; a mandatory list of
desktop environments is far too coarse a tool to deal with this.
Additionally, xdg-desktop-portal has to have enough fallbacks to ensure
that, if it cannot find any implementation for the current desktop, it will
proxy to the first implementation it can find in order to give a meaningful
answer. Finally, since the supported desktops are shipped by the portal
themselves, there’s no way to override this information by packagers,
admins, or users.</p>
<p>After <a href="https://github.com/flatpak/xdg-desktop-portal/issues/906">iterating over the issue</a>, I ended up writing the
support for a new configuration file. Instead of having portals say what
kind of desktop environment they require, we have desktop environments
saying which portal implementations they prefer. Now, each desktop should
ship a <code>${NAME}-portals.conf</code> <span class="caps">INI</span>-like desktop entry file listing each
interface, and what kind of desktop portal should be used for it; for
instance, the <span class="caps">GNOME</span> desktop should ship a <code>gnome-portals.conf</code> configuration
file that specifies a default for every interface:</p>
<div class="highlight"><pre><span></span><code><span class="k">[preferred]</span>
<span class="na">default</span><span class="o">=</span><span class="s">gnome</span>
</code></pre></div>
<p>On the other hand, you could have a <em>Foo</em> desktop that relies on the <span class="caps">GTK</span>
portal for everything, except for specific interfaces that are implemented
by the “foo” portal:</p>
<div class="highlight"><pre><span></span><code><span class="k">[preferred]</span>
<span class="na">default</span><span class="o">=</span><span class="s">gtk</span>
<span class="na">org.freedesktop.impl.portal.Screenshot</span><span class="o">=</span><span class="s">foo</span>
<span class="na">org.freedesktop.impl.portal.Screencast</span><span class="o">=</span><span class="s">foo</span>
</code></pre></div>
<p>You could also disable all portals except for a specific interface (and its dependencies):</p>
<div class="highlight"><pre><span></span><code><span class="k">[preferred]</span>
<span class="na">default</span><span class="o">=</span><span class="s">none</span>
<span class="na">org.freedesktop.impl.portal.Account</span><span class="o">=</span><span class="s">gtk</span>
<span class="na">org.freedesktop.impl.portal.FileChooser</span><span class="o">=</span><span class="s">gtk</span>
<span class="na">org.freedesktop.impl.portal.Lockdown</span><span class="o">=</span><span class="s">gtk</span>
<span class="na">org.freedesktop.impl.portal.Settings</span><span class="o">=</span><span class="s">gtk</span>
</code></pre></div>
<p>Or, finally, you could disable all portal implementations:</p>
<div class="highlight"><pre><span></span><code><span class="k">[preferred]</span>
<span class="na">default</span><span class="o">=</span><span class="s">none</span>
</code></pre></div>
<p>A nice side effect of this work is that you can configure your own system,
by dropping a <code>portals.conf</code> configuration file inside the
<code>XDG_CONFIG_HOME/xdg-desktop-portal</code> directory; this should cover all the
cases in which people assemble their desktop out of disparate components.</p>
<p>By having desktop environments (or, in a pinch, the user themselves) owning
the kind of portals they require, we can avoid messy configurations in the
portal implementations, and clarify the intended behaviour to downstream
packagers; at the same time, generic portal implementations can be adopted
by multiple environments without necessarily having to know which ones upfront.</p>
<hr>
<p>In a way, the desktop portals project is trying to fulfill the original
mission of freedesktop.org’s Cross-desktop Group: a set of <span class="caps">API</span> that are not
bound to a single environment, and can be used to define “the Linux desktop”
as a platform.</p>
<p>Of course, there’s a lot of work involved in creating a vendor-neutral
platform <span class="caps">API</span>, especially when it comes to designing both the user and the
developer experiences; ideally, more people should be involved in this
effort, so if you want to contribute to the Linux ecosystem, this is an area
where you can make the difference.</p>Writing Bindable API, 2023 Edition2023-02-20T00:35:00+00:002023-02-22T13:09:08+00:00ebassitag:www.bassi.io,2023-02-20:/articles/2023/02/20/bindable-api-2023/<p>In which I make an attempt at encoding best introspection practices for <span class="caps">API</span> writers</p><p>First of all, you should go on the <a href="https://gi.readthedocs.io/en/latest/">gobject-introspection website</a>
and read the page on <a href="https://gi.readthedocs.io/en/latest/writingbindableapis.html">how to write bindable <span class="caps">API</span></a>. What
I’m going to write here is going to build upon what’s already documented, or
will update the best practices, so if you maintain a GObject/C library, or
you’re writing one, you <strong>must</strong> be familiar with the basics of
gobject-introspection. It’s 2023: it’s already too bad we’re still writing C
libraries, we should <em>at the very least</em> be responsible about it.</p>
<p>A specific note for people maintaining an existing GObject/C library with an
<span class="caps">API</span> designed <em>before</em> the mainstream establishment of gobject-introspection
(basically, anything written prior to 2011): you should <strong>really</strong> consider
writing all new types and entry points with gobject-introspection in mind,
and you should also consider phasing out older <span class="caps">API</span> and replacing it
piecemeal with a bindable one. You should have done this 10 years ago, and I
can already hear the objections, but: <em>too bad</em>. Just because you made an
effort 10 years ago it doesn’t mean things are frozen in time, and you don’t
get to fix things. Maintenance means constantly tending to your code, and
that doubly applies if you’re exposing an <span class="caps">API</span> to other people.</p>
<hr>
<p>Let’s take the “how to write bindable <span class="caps">API</span>” recommendations, and elaborate
them a bit.</p>
<h3>Structures with custom memory management</h3>
<p>The recommendation is to use <code>GBoxed</code> as a way to specify a copy and a free
function, in order to clearly define the memory management semantics of a type.</p>
<p>The important <em>caveat</em> is that boxed types are necessary for:</p>
<ul>
<li>opaque types that can only be heap allocated</li>
<li>using a type as a GObject property</li>
<li>using a type as an argument or return value for a GObject signal</li>
</ul>
<p>You don’t <strong>need</strong> a boxed type for the following cases:</p>
<ul>
<li>your type is an argument or return value for a method, function, or
virtual function</li>
<li>your type can be placed on the stack, or can be allocated with
<code>malloc()</code>/<code>free()</code></li>
</ul>
<p>Additionally, starting with gobject-introspection 1.76, you can specify the
copy and free function of a type <em>without</em> necessarily registering a boxed
type, which leaves boxed types for the thing they were created: signals and properties.</p>
<h4>Addendum: object types</h4>
<p>Boxed types should only ever be used for plain old data types; if you need
inheritance, then the strong recommendation is to use <code>GObject</code>. You can use
<code>GTypeInstance</code>, but <strong>only if you know what you’re doing</strong>; for more
information on that, see <a href="https://www.bassi.io/articles/2020/06/02/type-instances/">my old blog post about typed instances</a>.</p>
<h3>Functionality only accessible through a C macro</h3>
<p>This ought to be fairly uncontroversial. C pre-processor symbols don’t exist
at the <span class="caps">ABI</span> level, and gobject-introspection is a mechanism to describe a C
<span class="caps">ABI</span>. Never, <strong>ever</strong> expose <span class="caps">API</span> only through C macros; those are for C
developers. C macros can be used to create convenience wrappers, but
remember that anything they call must be public <span class="caps">API</span>, and that other people
will need to re-implement the convenience wrappers themselves, so don’t
overdo it. C developers deserve some convenience, but not at the expense of
everyone else.</p>
<h4>Addendum: inline functions</h4>
<p>Static inline functions are also not part of the introspectable <span class="caps">ABI</span> of a
library, because they cannot be used with <code>dlsym()</code>; you can provide inlined
functions for performance reasons, but remember to always provide their
non-inlined equivalent.</p>
<h3>Direct C structure access for objects</h3>
<p>Again, another fairly uncontroversial rule. You shouldn’t be putting
anything into an instance structure, as it makes your <span class="caps">API</span> harder to
future-proof, and direct access cannot do things like change notification,
or <a href="https://en.wikipedia.org/wiki/Memoization">memoization</a>.</p>
<p>Always provide accessor functions.</p>
<h3><code>va_list</code></h3>
<p>Variadic argument functions are mainly C convenience. Yes, some languages
can support them, but it’s a bad idea to have this kind of <span class="caps">API</span> exposed as
the only way to do things.</p>
<p>Any variadic argument function should have two additional variants:</p>
<ul>
<li>a vector based version, using C arrays (zero terminated, or with an
explicit length)</li>
<li>a <code>va_list</code> version, to be used when creating wrappers with variadic
arguments themselves</li>
</ul>
<p>The <code>va_list</code> variant is kind of optional, since not many people go around
writing variadic argument C wrappers, these days, but at the end of the day
you might be going to write an internal function that takes a <code>va_list</code>
anyway, so it’s not particularly strange to expose it as part of your public
<span class="caps">API</span>.</p>
<p>The vector-based variant, on the other hand, is fundamental.</p>
<p>Incidentally, if you’re using variadic arguments as a way to collect
similarly typed values, e.g.:</p>
<div class="highlight"><pre><span></span><code><span class="c1">// void</span>
<span class="c1">// some_object_method (SomeObject *self,</span>
<span class="c1">// ...) G_GNUC_NULL_TERMINATED</span>
<span class="n">some_object_method</span><span class="w"> </span><span class="p">(</span><span class="n">obj</span><span class="p">,</span><span class="w"> </span><span class="s">"foo"</span><span class="p">,</span><span class="w"> </span><span class="s">"bar"</span><span class="p">,</span><span class="w"> </span><span class="s">"baz"</span><span class="p">,</span><span class="w"> </span><span class="nb">NULL</span><span class="p">);</span>
</code></pre></div>
<p>there’s very little difference to using a vector and C99’s compound literals:</p>
<div class="highlight"><pre><span></span><code><span class="c1">// void</span>
<span class="c1">// some_object_method (SomeObject *self,</span>
<span class="c1">// const char *args[])</span>
<span class="n">some_object_method</span><span class="w"> </span><span class="p">(</span><span class="n">obj</span><span class="p">,</span><span class="w"> </span><span class="p">(</span><span class="k">const</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="p">[])</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="s">"foo"</span><span class="p">,</span>
<span class="w"> </span><span class="s">"bar"</span><span class="p">,</span>
<span class="w"> </span><span class="s">"baz"</span><span class="p">,</span>
<span class="w"> </span><span class="nb">NULL</span><span class="p">,</span>
<span class="w"> </span><span class="p">});</span>
</code></pre></div>
<p>Except that now the compiler will be able to do some basic type check and
scream at you if you’re doing something egregiously bad.</p>
<p>Compound literals and designated initialisers also help when dealing with
key/value pairs:</p>
<div class="highlight"><pre><span></span><code><span class="k">typedef</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">column</span><span class="p">;</span>
<span class="w"> </span><span class="k">union</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">v_str</span><span class="p">;</span>
<span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">v_int</span><span class="p">;</span>
<span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="n">value</span><span class="p">;</span>
<span class="p">}</span><span class="w"> </span><span class="n">ColumnValue</span><span class="p">;</span>
<span class="k">enum</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">COLUMN_NAME</span><span class="p">,</span>
<span class="w"> </span><span class="n">COLUMN_AGE</span><span class="p">,</span>
<span class="w"> </span><span class="n">N_COLUMNS</span>
<span class="p">};</span>
<span class="c1">// void</span>
<span class="c1">// some_object_method (SomeObject *self,</span>
<span class="c1">// size_t n_columns,</span>
<span class="c1">// const ColumnValue values[])</span>
<span class="n">some_object_method</span><span class="w"> </span><span class="p">(</span><span class="n">obj</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span>
<span class="w"> </span><span class="p">(</span><span class="n">ColumnValue</span><span class="w"> </span><span class="p">[])</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">.</span><span class="n">column</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">COLUMN_NAME</span><span class="p">,</span><span class="w"> </span><span class="p">.</span><span class="n">data</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">.</span><span class="n">v_str</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">"Emmanuele"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">.</span><span class="n">column</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">COLUMN_AGE</span><span class="p">,</span><span class="w"> </span><span class="p">.</span><span class="n">data</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">.</span><span class="n">v_int</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">42</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">},</span>
<span class="w"> </span><span class="p">});</span>
</code></pre></div>
<p>So you should seriously reconsider the amount of variadic arguments
convenience functions you expose.</p>
<h3>Multiple out parameters</h3>
<p>Using a structured type with a <code>out</code> direction is a good recommendation as a
way to both limit the amount of <code>out</code> arguments <em>and</em> provide some
future-proofing for your <span class="caps">API</span>. It’s easy to expand an opaque pointer type
with accessors, whereas adding more <code>out</code> arguments requires an <span class="caps">ABI</span> break.</p>
<h4>Addendum: <code>inout</code> arguments</h4>
<p>Don’t use in-out arguments. Just don’t.</p>
<p>Pass an <code>in</code> argument to the callable for its input, and take an <code>out</code>
argument or a return value for the output.</p>
<p>Memory management and ownership of <code>inout</code> arguments is <em>incredibly</em> hard to
capture with static annotations; it mainly works for scalar values, so:</p>
<div class="highlight"><pre><span></span><code><span class="kt">void</span>
<span class="n">some_object_update_matrix</span><span class="w"> </span><span class="p">(</span><span class="n">SomeObject</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="p">,</span>
<span class="w"> </span><span class="kt">double</span><span class="w"> </span><span class="o">*</span><span class="n">xx</span><span class="p">,</span>
<span class="w"> </span><span class="kt">double</span><span class="w"> </span><span class="o">*</span><span class="n">yy</span><span class="p">,</span>
<span class="w"> </span><span class="kt">double</span><span class="w"> </span><span class="o">*</span><span class="n">xy</span><span class="p">,</span>
<span class="w"> </span><span class="kt">double</span><span class="w"> </span><span class="o">*</span><span class="n">yx</span><span class="p">)</span>
</code></pre></div>
<p>can work with <code>xx</code>, <code>yy</code>, <code>xy</code>, <code>yx</code> as <code>inout</code> arguments, because there’s
no ownership transfer; but as soon as you start throwing things in like
pointers to structures, or vectors of string, you open yourself to questions like:</p>
<ul>
<li>who allocates the argument when it goes in?</li>
<li>who is responsible for freeing the argument when it comes out?</li>
<li>what happens if the function frees the argument in the <code>in</code> direction and
then re-allocates the <code>out</code>?</li>
<li>what happens if the function uses a different allocator than the one used
by the caller?</li>
<li>what happens if the function has to allocate more memory?</li>
<li>what happens if the function modifies the argument and frees memory?</li>
</ul>
<p>Even if gobject-introspection nailed down the rules, they could not be
enforced, or validated, and could lead to leaks or, worse, crashes.</p>
<p>So, once again: don’t use <code>inout</code> arguments. If your <span class="caps">API</span> already exposes
<code>inout</code> arguments, especially for non-scalar types, consider deprecations
and adding new entry points.</p>
<h4>Addendum: <code>GValue</code></h4>
<p>Sadly, <code>GValue</code> is one of the most notable cases of <code>inout</code> abuse. The
oldest parts of the <span class="caps">GNOME</span> stack use <code>GValue</code> in a way that requires <code>inout</code>
annotations because they expect the caller to:</p>
<ul>
<li>initialise a <code>GValue</code> with the desired type</li>
<li>pass the address of the value</li>
<li>let the function fill the value</li>
</ul>
<p>The caller is then left with calling <code>g_value_unset()</code> in order to free the
resources associated with a <code>GValue</code>. This means that you’re passing an
initialised value to a callable, the callable will do something to it (which
may or may not even entail re-allocating the value) and then you’re going to
get it back at the same address.</p>
<p>It would be a lot easier if the <span class="caps">API</span> left the job of initialising the
<code>GValue</code> to the callee; then functions could annotate the <code>GValue</code> argument
with <code>out</code> and <code>caller-allocates=1</code>. This would leave the ownership to the
caller, and remove a whole lot of uncertainty.</p>
<p>Various new (comparatively speaking) <span class="caps">API</span> allow the caller to pass an
unitialised <code>GValue</code>, and will leave initialisation to the callee, which is
how it should be, but this kind of change isn’t always possible in a
backward compatible way.</p>
<h3>Arrays</h3>
<p>You can use three types of C arrays in your <span class="caps">API</span>:</p>
<ul>
<li>zero-terminated arrays, which are the easiest to use, especially for
pointers and strings</li>
<li>fixed-size arrays</li>
<li>arrays with length arguments</li>
</ul>
<h4>Addendum: strings and byte arrays</h4>
<p>A <code>const char*</code> argument for C strings with a length argument is <strong>not</strong> an array:</p>
<div class="highlight"><pre><span></span><code><span class="cm">/**</span>
<span class="cm"> * some_object_load_data:</span>
<span class="cm"> * @self: ...</span>
<span class="cm"> * @str: the data to load</span>
<span class="cm"> * @len: length of @str in bytes, or -1</span>
<span class="cm"> *</span>
<span class="cm"> * ...</span>
<span class="cm"> */</span>
<span class="kt">void</span>
<span class="n">some_object_load_data</span><span class="w"> </span><span class="p">(</span><span class="n">SomeObject</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="p">,</span>
<span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">str</span><span class="p">,</span>
<span class="w"> </span><span class="kt">ssize_t</span><span class="w"> </span><span class="n">len</span><span class="p">)</span>
</code></pre></div>
<p><strong>Never</strong> annotate the <code>str</code> argument with <code>array length=len</code>. Ideally, this
kind of function <em>should not exist in the first place</em>. You should always
use <code>const char*</code> for <code>NUL</code>-terminated strings, possibly <span class="caps">UTF</span>-8 encoded; if
you allow embedded <code>NUL</code> characters then use a bytes array:</p>
<div class="highlight"><pre><span></span><code><span class="cm">/**</span>
<span class="cm"> * some_object_load_data:</span>
<span class="cm"> * @self: ...</span>
<span class="cm"> * @data: (array length=len) (element-type uint8): the data to load</span>
<span class="cm"> * @len: the length of the data in bytes</span>
<span class="cm"> *</span>
<span class="cm"> * ...</span>
<span class="cm"> */</span>
<span class="kt">void</span>
<span class="n">some_object_load_data</span><span class="w"> </span><span class="p">(</span><span class="n">SomeObject</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="p">,</span>
<span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="kt">unsigned</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">data</span><span class="p">,</span>
<span class="w"> </span><span class="kt">size_t</span><span class="w"> </span><span class="n">len</span><span class="p">)</span>
</code></pre></div>
<p>Instead of <code>unsigned char</code> you can also use <code>uint8_t</code>, just to drive the
point home.</p>
<p>Yes, it’s slightly nicer to have a single entry point for strings and byte
arrays, but that’s just a C convenience: decent languages will have a proper
string type, which always comes with a length; and string types are not
binary data.</p>
<h4>Addendum: <code>GArray</code>, <code>GPtrArray</code>, <code>GByteArray</code></h4>
<p>Whatever you do, however low you feel on the day, whatever particular
tragedy befell your family at some point, please: <strong>never</strong> use GLib array
types in your <span class="caps">API</span>. Nothing good will ever come of it, and you’ll just spend
your days regretting this choice.</p>
<p>Yes: gobject-introspection transparently converts between GLib array types
and C types, to the point of allowing you to annotate the contents of the
array. The problem is that that information is static, and only exists at
the introspection level. There’s nothing that prevents you from putting
other random data into a <code>GPtrArray</code>, as long as it’s pointer-sized.
There’s nothing that prevents a version of a library from saying that you
own the data inside a <code>GArray</code>, and have the next version assign a clear
function to the array to avoid leaking it all over the place on error
conditions, or when using <code>g_autoptr</code>.</p>
<p>Adding support for GLib array types in the introspection was a
well-intentioned mistake that worked in very specific cases—for instance, in
a library that is private to an application. Any well-behaved, well-designed
general purpose library should not expose this kind of <span class="caps">API</span> to its consumers.</p>
<p>You should use <code>GArray</code>, <code>GPtrArray</code>, and <code>GByteArray</code> internally; they are
good types, and remove a lot of the pain of dealing with C arrays. Those
types should never be exposed at the <span class="caps">API</span> boundary: always convert them to C
arrays, or wrap them into your own data types, with proper argument
validation and ownership rules.</p>
<h4>Addendum: <code>GHashTable</code></h4>
<p>What’s worse than a type that contains data with unclear ownership rules
decided at run time? A type that contains twice the amount of data with
unclear ownership rules decided at run time.</p>
<p>Just like the GLib array types, hash tables should be used but never
directly exposed to consumers of an <span class="caps">API</span>.</p>
<h4>Addendum: <code>GList</code>, <code>GSList</code>, <code>GQueue</code></h4>
<p>See above, re: pain and misery. On top of that, linked lists are a
<strong>terrible</strong> data type that people should rarely, if ever, use in the first place.</p>
<h3>Callbacks</h3>
<p>Your callbacks should always be in the form of a simple callable with a
data argument:</p>
<div class="highlight"><pre><span></span><code><span class="k">typedef</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="p">(</span><span class="o">*</span><span class="w"> </span><span class="n">SomeCallback</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">SomeObject</span><span class="w"> </span><span class="o">*</span><span class="n">obj</span><span class="p">,</span>
<span class="w"> </span><span class="n">gpointer</span><span class="w"> </span><span class="n">data</span><span class="p">);</span>
</code></pre></div>
<p>Any function that takes a callback should also take a “user data” argument
that will be passed <em>as is</em> to the callback:</p>
<div class="highlight"><pre><span></span><code><span class="c1">// scope: call; the callback data is valid until the</span>
<span class="c1">// function returns</span>
<span class="kt">void</span>
<span class="nf">some_object_do_stuff_immediately</span><span class="w"> </span><span class="p">(</span><span class="n">SomeObject</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="p">,</span>
<span class="w"> </span><span class="n">SomeCallback</span><span class="w"> </span><span class="n">callback</span><span class="p">,</span>
<span class="w"> </span><span class="n">gpointer</span><span class="w"> </span><span class="n">data</span><span class="p">);</span>
<span class="c1">// scope: notify; the callback data is valid until the</span>
<span class="c1">// notify function gets called</span>
<span class="kt">void</span>
<span class="nf">some_object_do_stuff_with_a_delay</span><span class="w"> </span><span class="p">(</span><span class="n">SomeObject</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="p">,</span>
<span class="w"> </span><span class="n">SomeCallback</span><span class="w"> </span><span class="n">callback</span><span class="p">,</span>
<span class="w"> </span><span class="n">gpointer</span><span class="w"> </span><span class="n">data</span><span class="p">,</span>
<span class="w"> </span><span class="n">GDestroyNotify</span><span class="w"> </span><span class="n">notify</span><span class="p">);</span>
<span class="c1">// scope: async; the callback data is valid until the async</span>
<span class="c1">// callback is called</span>
<span class="kt">void</span>
<span class="nf">some_object_do_stuff_but_async</span><span class="w"> </span><span class="p">(</span><span class="n">SomeObject</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="p">,</span>
<span class="w"> </span><span class="n">GCancellable</span><span class="w"> </span><span class="o">*</span><span class="n">cancellable</span><span class="p">,</span>
<span class="w"> </span><span class="n">GAsyncReadyCallback</span><span class="w"> </span><span class="n">callback</span><span class="p">,</span>
<span class="w"> </span><span class="n">gpointer</span><span class="w"> </span><span class="n">data</span><span class="p">);</span>
<span class="c1">// not pictured here: scope forever; the data is valid fori</span>
<span class="c1">// the entirety of the process lifetime</span>
</code></pre></div>
<p>If your function takes more than one callback argument, you should make sure
that it also takes a different user data for each callback, and that the
lifetime of the callbacks are well defined. The alternative is to use
<code>GClosure</code> instead of a simple C function pointer—but that comes at a cost
of <code>GValue</code> marshalling, so the recommendation is to stick with one callback
per function.</p>
<h4>Addendum: the <code>closure</code> annotation</h4>
<p>It seems that many people are unclear about the <code>closure</code> annotation.</p>
<p>Whenever you’re describing a function that takes a callback, you should
<strong>always</strong> annotate the <em>callback</em> argument with the argument that contains
the <em>user data</em> using the <code>(closure argument)</code> annotation, e.g.</p>
<div class="highlight"><pre><span></span><code><span class="cm">/**</span>
<span class="cm"> * some_object_do_stuff_immediately:</span>
<span class="cm"> * @self: ...</span>
<span class="cm"> * @callback: (scope call) (closure data): the callback</span>
<span class="cm"> * @data: the data to be passed to the @callback</span>
<span class="cm"> *</span>
<span class="cm"> * ...</span>
<span class="cm"> */</span>
</code></pre></div>
<p>You should <strong>not</strong> annotate the <code>data</code> argument with a unary <code>(closure)</code>.</p>
<p>The unary <code>(closure)</code> is meant to be used when annotating the <strong>callback
type</strong>:</p>
<div class="highlight"><pre><span></span><code><span class="cm">/**</span>
<span class="cm"> * SomeCallback:</span>
<span class="cm"> * @self: ...</span>
<span class="cm"> * @data: (closure): ...</span>
<span class="cm"> *</span>
<span class="cm"> * ...</span>
<span class="cm"> */</span>
<span class="k">typedef</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="p">(</span><span class="o">*</span><span class="w"> </span><span class="n">SomeCallback</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">SomeObject</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="p">,</span>
<span class="w"> </span><span class="n">gpointer</span><span class="w"> </span><span class="n">data</span><span class="p">);</span>
</code></pre></div>
<p>Yes, it’s confusing, I know.</p>
<p>Sadly, the introspection parser isn’t very clear about this, but in the
future it will emit a warning if it finds a unary <code>closure</code> on anything that
isn’t a callback type.</p>
<p>Ideally, you don’t really need to annotate anything when you call your
argument <code>user_data</code>, but it does not hurt to be explicit.</p>
<hr>
<p>A cleaned up version of this blog post will go up on the
gobject-introspection website, and we should really have a proper set of
best <span class="caps">API</span> design practices on the Developer Documentation website by now;
nevertheless, I do hope people will actually follow these recommendations at
some point, and that they will be prepared for new recommendations in the
future. Only dead and unmaintained projects don’t change, after all, and I
expect the <span class="caps">GNOME</span> stack to last a bit longer than the 25 years it already
spans today.</p>On PyGObject2022-12-02T18:03:00+00:002022-12-02T18:56:00+00:00ebassitag:www.bassi.io,2022-12-02:/articles/2022/12/02/on-pygobject/<p>In which I look at the state of the Python bindings for the <span class="caps">GNOME</span> platform</p><p>Okay, I can’t believe I have to do this <a href="https://www.bassi.io/articles/2017/02/13/on-vala/"><strong>again</strong></a>.</p>
<p>This time, let’s leave <a href="https://twitter.com">the Hellsite</a> out of it, and
let’s try to be nuanced from the start. I’d like to avoid getting grief online.</p>
<blockquote>
<p>The current state of the Python bindings for GObject-based libraries is
making it really hard to recommend using Python as a language for
developing <span class="caps">GTK</span> and <span class="caps">GNOME</span> applications.</p>
</blockquote>
<p>PyGObject is currently <em>under</em>maintained, even after the heroic efforts of
Christoph Reiter to keep the fires burning through the long night. The
Python community needs more people to work on the bindings, if we want
Python to be a first class citizen of the ecosystem.</p>
<p>There’s a lot to do, and not nearly enough people left to do it.</p>
<h3>Case study: typed instances</h3>
<p>Yes, <a href="https://www.bassi.io/articles/2020/06/02/type-instances/"><em>thou shall use GObject</em></a> should be the
law of the land; <strong>but</strong> there are legitimate reasons to use typed
instances, and <span class="caps">GTK</span> 4 has a few of them:</p>
<ul>
<li><a href="https://docs.gtk.org/gtk4/class.Expression.html"><code>GtkExpression</code></a></li>
<li><a href="https://docs.gtk.org/gsk4/class.RenderNode.html"><code>GskRenderNode</code></a></li>
<li><a href="https://docs.gtk.org/gdk4/class.Event.html"><code>GdkEvent</code></a></li>
</ul>
<p>At this very moment, it is impossible to use the types above from Python.
PyGObject will literally error out if you try to do so. There are technical
reasons why that was a reasonable choice 15+ years ago, but most language
bindings written since then can handle typed instances just fine. In fact,
PyGObject <em>does</em> handle them, since
<a href="https://docs.gtk.org/gobject/class.ParamSpec.html"><code>GParamSpec</code></a> is a
<code>GTypeInstance</code>; of course, that’s because PyGObject has some <em>ad hoc</em> code
for them.</p>
<p>Dealing with events and render nodes is not so important in <span class="caps">GTK4</span>; but not
having access to the expressions <span class="caps">API</span> makes writing list widgets incredibly
more complicated, requiring to set up everything through <span class="caps">UI</span> definition files
and never modifying the objects programmatically.</p>
<h3>Case study: constructing and disposing</h3>
<p>While most of the <span class="caps">API</span> in PyGObject is built through introspection, the base
wrapper for the GObject class is still very much written in CPython. This
requires, among other things, wiring the class’s virtual functions manually,
and figuring out the interactions between Python, GObject, and the type
system wrappers. This means Python types that inherit from GObject don’t
have automatic access to the <code>GObjectClass.constructed</code> and
<code>GObjectClass.dispose</code> virtual functions. Normally, this would not be an
issue, but modern GObject-based libraries have started to depend on being
able to control construction and destruction sequences.</p>
<p>For instance, it is necessary for any type that inherits from <code>GtkWidget</code> to
ensure that all its child widgets are disposed manually. While that was
possible through the “destroy” signal in <span class="caps">GTK3</span>, in <span class="caps">GTK4</span> the signal was
removed and everything should go through the <code>GObjectClass.dispose</code> virtual
function. Since PyGObject does not allow overriding or implementing that
virtual function, your Python class cannot inherit from <code>GtkWidget</code>, but
must inherit from ancillary classes like <code>GtkBox</code> or <code>AdwBin</code>. That’s even
more relevant for the disposal of resources created through the <a href="https://pygobject.readthedocs.io/en/latest/guide/gtk_template.html">composite
template <span class="caps">API</span></a>.
Theoretically, using <code>Gtk.Template</code> would take care of this, but since we
cannot plug the Python code into the underlying CPython, we’re stuck.</p>
<h3>Case study: documentation, examples, and tutorials</h3>
<p>While I have been trying to write Python examples for the <a href="https://developer.gnome.org/documentation"><span class="caps">GNOME</span> developers
documentation</a>, I cannot write
<em>everything</em> by myself. Plus, I can’t convince Google to only link to what I
write. The result is that searching for “Python” and “<span class="caps">GNOME</span>” or “<span class="caps">GTK</span>” will
inevitably lead people to the <span class="caps">GTK3</span> tutorials and references.</p>
<p>The fragmentation of the documentation is also an issue. The <a href="https://pygobject.readthedocs.io">PyGObject
website</a> is off to readthedocs.org, which
is understandable for a Python projects; then we have the <a href="https://python-gtk-3-tutorial.readthedocs.io/en/latest/"><span class="caps">GTK3</span>
tutorial</a>, which
hasn’t been updated in a while, and for which <a href="https://github.com/sebp/PyGObject-Tutorial/issues/191">there are no plans to have a
<span class="caps">GTK</span> 4 version</a>.</p>
<p>Additionally, the <a href="http://lazka.github.io/pgi-docs/">Python <span class="caps">API</span> reference</a>
is currently stuck on <span class="caps">GTK3</span>, with <a href="https://amolenaar.github.io/pgi-docgen/">Python references for <span class="caps">GTK4</span> and
friends</a> off on the side.</p>
<p>It would be great to unify the reference sites, and possibly have them under
the <span class="caps">GNOME</span> infrastructure; but at this point, I’d settle for having a single
place to find everything, like <a href="https://gjs.guide"><span class="caps">GJS</span> did</a>.</p>
<h2>What can <strong>you</strong> do?</h2>
<p>Pick up <a href="https://gitlab.gnome.org/GNOME/pygobject">PyGObject</a>. Learn how it
works, and how the GObject and CPython <span class="caps">API</span> interact.</p>
<p>Write Python overrides to ensure that <span class="caps">API</span> like <span class="caps">GTK</span> and <span class="caps">GIO</span> are nice to use
with idiomatic Python.</p>
<p>Write tests for PyGObject.</p>
<p>Write documentation.</p>
<p>Port existing tutorials, examples, and demos to <span class="caps">GTK4</span>.</p>
<p>Help Christoph out when it comes to triaging issues, and reviewing merge requests.</p>
<p>Join <a href="https://matrix.to/#/#python:gnome.org"><span class="caps">GNOME</span> Python on Matrix</a>, or the
<code>#gnome-python</code> channel on <a href="https://libera.chat">Libera</a>.</p>
<p>Ask questions and provide answers on
<a href="https://discourse.gnome.org/tag/python">Discourse</a>.</p>
<h2>What happens if nobody does anything?</h2>
<p>Right now, we’re in maintenance mode. Things work because of inertia, and
because nobody is <em>really</em> pushing the bindings outside of their existing functionality.</p>
<p>Let’s be positive, for a change, and assume that people <em>will</em> show up. They
did for Vala when I ranted about it five years ago after a particularly
frustrating week dealing with constant build failures in <span class="caps">GNOME</span>, so maybe the
magic will happen again.</p>
<p>If people do <em>not</em> show up, though, what will likely happen is that Python
will just fall on the wayside inside <span class="caps">GNOME</span>. Python developers won’t use <span class="caps">GTK</span>
to write <span class="caps">GUI</span> applications; existing Python applications targeting <span class="caps">GNOME</span> will
either wither and die, or get ported to other languages. The Python bindings
themselves may stop working with newer versions of Python, which will
inevitably lead downstream distributors to jettison the bindings themselves.</p>
<p>We have been through this dance with the C# bindings and Mono, and the <span class="caps">GNOME</span>
and Mono communities are all the poorer for it, so I’d like to avoid losing
<em>another</em> community of talented developers. History does not need to repeat itself.</p>Amberol2022-05-25T01:30:00+01:002022-06-16T13:46:57+01:00ebassitag:www.bassi.io,2022-05-25:/articles/2022/05/25/amberol/<p>In which I write a music player mostly for me</p><h4>In the beginning…</h4>
<p>In 1997, I downloaded my first <span class="caps">MP3</span> file. It was linked on a website, and all
I had was a 56k modem, so it took me ages to download the nearly 4 megabytes
of 128 kbit/s <a href="https://www.youtube.com/watch?v=bPdisSAev0A">music goodness</a>.
Before that file magically appeared on my hard drive, if we exclude a brief
dalliance with <span class="caps">MOD</span> files, the only music I had on my computer came either in
<span class="caps">MIDI</span> or in <span class="caps">WAV</span> format.</p>
<p>In the nearly 25 years passed since that seminal moment, my music collection
has steadily increased in size — to the point that I cannot comfortably keep
it in my laptop’s internal storage without cutting into the available space
for other stuff and without taking ages when copying it to new machines; and
if I had to upload it to a cloud service, I’d end up paying monthly storage
fees that would definitely not make me happy. Plus, I like being able to
listen to my music without having a network connection — say, when I’m
travelling. For these reasons, I have my music collection on a dedicated
<span class="caps">USB3</span> drive and on various 128 <span class="caps">GB</span> <span class="caps">SD</span> cards that I use when travelling, to
avoid bumping around a spinning rust drive.</p>
<p>In order to listen to that first <span class="caps">MP3</span> file, I also had to download a music
player, and back in 1997 there was this little software called
<a href="https://en.wikipedia.org/wiki/Winamp">Winamp</a>, which apparently <em>really whipped the llama’s ass</em>.
Around that same time I was also dual-booting between Windows and Linux,
and, obviously, Linux had its own Winamp clone called <a href="https://en.wikipedia.org/wiki/XMMS">x11amp</a>.
This means that, since late 1997, I’ve also tested more or less all
mainstream, <span class="caps">GTK</span>-based Linux music players—xmms, beep, xmms2, Rhythmbox,
Muine, Banshee, Lollypop, <span class="caps">GNOME</span> Music—and various less mainstream/non-<span class="caps">GTK</span>
ones—shout out to <em>ma boi</em> mpg123. I also used iTunes on macOS and Windows,
but I don’t speak of that.</p>
<p>Turns out that, with the very special exception of Muine, I can’t stand any
of them. They are all fairly inefficient when it comes to managing my music
collection; or they are barely maintained; or (but, most often, and) they
are just iTunes clones—as if cloning iTunes was a worthy goal for anything
remotely connected to music, computing, or even human progress in general.</p>
<p>I did enjoy using Banshee, up to a point; it wasn’t overly offensive to my
eyes and pointing devices, and had the advantage of being able to minimise
its <span class="caps">UI</span> without getting in the way. It just bitrotted with the rest of the
<span class="caps">GNOME</span> 2 platform even before <span class="caps">GNOME</span> bumped major version, and it still wasn’t
as good as Muine.</p>
<h4>A detour: managing a music collection</h4>
<p><em>I’d like to preface this detour with a disclaimer: I am not talking about
specific applications; specific technologies/libraries; or specific
platforms. Any resemblance to real projects, existing or abandoned, is
purely coincidental. Seriously.</em></p>
<p>Most music management software is, I feel, predicated on the fallacy that
the majority of people don’t bother organising their files, and are thus
willing to accept a flat storage with complex views built at run time on top
of that; while simultaneously being willing to spend a disproportionate
amount of time classifying those files—without, of course, using a
hierarchical structure. This is a fundamental misunderstanding of human nature.</p>
<p>By way of an example: if we perceive the Universe in a techno-mazdeist
struggle between a πνεῦμα which creates fool-proof tools for users; and a
φύσις, which creates more and more adept fools; then we can easily see that,
for the entirety of history until now, the <em>pneuma</em> has been kicked squarely
in the nuts by the <em>physis</em>. In other words: any design or implementation
that does not take into account human nature in that particular problem
space is bound to fail.</p>
<p>While documents <em>might</em> benefit from additional relations that are not
simply inferred by their type or location on the file system, media files do
not really have the same constraints. <em>Especially</em> stuff like music or
videos. All the tracks of an album are in the same place not because I
decided that, but because the artist or the music producers willed it that
way; all the episodes of a series are in the same place because of course
they are, and they are divided by season because that’s how <span class="caps">TV</span> series work;
all the episodes of a podcast are in the same place for the same reason,
maybe divided by year, or by season. If that structure already exists, then
what’s the point of flattening it and then trying to recreate it every time
out of thin air with a database query?</p>
<p>The end result of constructing a <span class="caps">UI</span> that is just a view on top of a database
is that your <span class="caps">UI</span> will be indistiguishable from a database design and
management tool; which is why all music management software looks very much
like Microsoft Access from circa 1997 onwards. Of course you can dress it up
however you like, by adding fancy views of album covers, but at the end of
the day it’s just an Excel spread sheet that <em>occasionally</em> plays music.</p>
<p>Another side effect of writing a database that contains the metadata of a
bunch of files is that you’ll end up changing the database instead of
changing the files; you could write the changes to the files, but
reconciling the files with the database is a hard problem, and it also
assumes you have read-write access to those files. Now that you have locked
your users into your own database, switching to a new application becomes
harder, unless your users enjoy figuring out what they changed over time.</p>
<p>A few years ago, before backing up everything in three separate storages,
I had a catastrophic failure on my primary music hard drive; after
recovering most of my data, I realised that <em>a lot</em> of the changes I made in
the early years weren’t written out to music files, but were stored in some
random SQLite database somewhere. I am still recovering from that particular disaster.</p>
<p>I want my music player to have read-only access to my music. I don’t want
anything that isn’t me writing to it. I also don’t want to re-index my whole
music collection just because I fixed the metadata of one album, and I don’t
want to lose all my changes when I find a better music player.</p>
<h4>Another detour: non-local media</h4>
<p>Yes, yes: everyone listens to streamed media these days, because media (and
software) companies are speed-running Adam Smith’s <em>The Wealth of Nations</em>
and have just arrived at the bit about rentier economy. After all, why
should they want to get paid once for something, when media conglomerates
can “reap where they never sowed, and demand a rent even for its natural produce”.</p>
<p>You know what streaming services don’t like? Custom, third party clients
that they can’t control, can’t use for metrics, and can’t use to serve
people ads.</p>
<p>You know what cloud services that offer to host music don’t like? Duplicate
storage, and service files that may potentially infringe the <span class="caps">IP</span> of a very
litigious industry. Plus, of course, third party clients that they can’t use
to serve you ads, as that’s how they can operate at all, because this is the
Darkest Timeline, and adtech is the modern Moloch to which we must sacrifice
as many lives as we can.</p>
<p>You may have a music player that streams somebody’s music collection, or
even yours if you can accept the remote service making a mess of it, but
you’re always a bad <span class="caps">IPO</span> or a bad quarterly revenue report away from losing
access to everything.</p>
<h4>Writing a music player for fun and no profit</h4>
<p>For the past few years I’ve been meaning to put some time into writing a
music player, mostly for my own amusement; I also had the idea of using this
project to learn the <a href="https://www.rust-lang.org">Rust programming language</a>.
In 2015 I was looking for a way to read the metadata of music files with
Rust, but since I couldn’t find anything decent, I ended up writing the Rust
<a href="https://github.com/ebassi/taglib-rust">bindings for taglib</a>. I kept
noodling at this side project for the following years, but I was mostly
hitting the limits of <span class="caps">GTK3</span> when it came to dealing with my music collection;
every single iteration of the user interface ended up with a GtkTreeView and
a replica of iTunes 1.0.</p>
<p>In the meantime, though, the Rust ecosystem got exponentially better, with
lots of crates dedicated to parsing music file metadata; <span class="caps">GTK4</span> got released
with <a href="https://docs.gtk.org/gtk4/section-list-widget.html">new list widgets</a>;
libadwaita is available to take care of nice <span class="caps">UI</span> layouts; and the Rust
bindings for <span class="caps">GTK</span> have become one of the most well curated and maintained
projects in the language bindings ecosystem.</p>
<p>Another few things that happened in the meantime: a pandemic, a year of
unemployment, and zero conferences, all of which pushed me to streaming my
free and open source software contributions on
<a href="https://twitch.tv/ebassi">Twitch</a>, as a way to break the isolation.</p>
<p>So, after spending the first couple of months of 2022 on writing the
beginners tutorial for the <span class="caps">GNOME</span> developer documentation website, in March
I began writing <a href="https://gitlab.gnome.org/World/amberol">Amberol</a>, a
local-only music player that has no plans of becoming more than that.</p>
<p><figure>
<figcaption class="image-caption">
<p>Desktop mode</p>
</figcaption>
<div><img src="https://www.bassi.io/images/amberol-1.png"/></div>
</figure></p>
<p>Amberol’s scope sits in the same grand tradition of Winamp, and while its
<span class="caps">UI</span> started off as a Muine rip off—down to the same key shortcuts—it has
evolved into something that more closely resembles the music player I have
on my phone.</p>
<p><figure>
<figcaption class="image-caption">
<p>Mobile mode</p>
</figcaption>
<div><img src="https://www.bassi.io/images/amberol-2.png"/></div>
</figure></p>
<p>Amberol’s explicit goal is to let me play music on my desktop the same way I
typically do when I am using my phone, which is: shuffling all the songs in
my music collection; or, alternatively, listening to all the songs in an
album or from an artist from start to finish.</p>
<p>Amberol’s explicit <em>non goals</em> are:</p>
<ul>
<li>managing your music collection</li>
<li>figuring out your music metadata</li>
<li>building playlists</li>
<li>accessing external services for stuff like cover art, song lyrics, or the
artist’s Wikipedia page</li>
</ul>
<p>The <em>actual</em> main feature of this application is that it has forced me to
figure out how to deal with GStreamer after 15 years.</p>
<p>I did try to write this application in a way that reflects the latest best
practices of <span class="caps">GTK4</span>:</p>
<ul>
<li>model objects</li>
<li>custom view widgets</li>
<li>composite widgets using templates</li>
<li>property bindings/expressions to couple model/state to its view/representation</li>
<li>actions and actionable widgets</li>
</ul>
<p>The ability to rely on libadwaita has allowed me to implement the recoloring
of the main window without having to deal with breakage coming from rando
style sheets:</p>
<p><span class="videobox">
<video width="100%" height="480" preload="none" controls poster="None"><source src='https://www.bassi.io/images/amberol-recolor-1.mp4' type='video/mp4; codecs="avc1.42E01E, mp4a.40.2"'></video></span></p>
<p>The main thing I did not expect was how much of a good fit was Rust in all
of this. The <a href="https://gtk-rs.org"><span class="caps">GTK</span> bindings</a> are top notch, and
constantly improving; the type system has helped me much more than hindering
me, a poor programmer whose mind has been twisted by nearly two decades of
C. Good idiomatic practices for <span class="caps">GTK</span> are entirely within the same ballpark of
idiomatic practices for Rust, especially for application development.</p>
<p>On the tooling side, Builder has been incredibly helpful in letting me
concentrate on the project—starting from the basic template for a <span class="caps">GNOME</span>
application in Rust, to dealing with the build system; from the Flatpak
manifest, to running the application under a debugger. My work was basically
ready to be submitted to
<a href="https://flathub.org/apps/details/io.bassi.Amberol">Flathub</a> from day one. I
did have some challenge with the AppData validation, mostly caused by
appstream-utils undocumented validation rules, but luckily it’s entirely
possible to remove the validation after you deal with the basic manifest.</p>
<p>All in all, I am definitely happy with the results of basically two months
of hacking and refactoring, mostly off an on (and with two weeks of <span class="caps">COVID</span> in
the middle).</p>
<div style="text-align:center">
<a style="text-decoration:none" href="https://flathub.org/apps/details/io.bassi.Amberol"><img src="https://flathub.org/assets/badges/flathub-badge-en.png" style="width:200px" width="200"/></a>
</div>Fair Weather Friends2021-11-19T20:48:00+01:002023-02-20T00:31:37+00:00ebassitag:www.bassi.io,2021-11-19:/articles/2021/11/19/fair-weather-friends/<p>In which I announce the release of the first GWeather-4 developers snapshot</p><p>Today I released
<a href="https://gitlab.gnome.org/GNOME/libgweather/-/releases/3.90.0">libgweather-3.90.0</a>,
the first developers snapshot of GWeather 4:</p>
<p><figure>
<figcaption class="image-caption">
<p>Behold! A project logo</p>
</figcaption>
<div><img src="https://www.bassi.io/images/gweather-docs.png"/></div>
</figure></p>
<p>This release is mostly meant to be used as a target for porting existing
code to the new <span class="caps">API</span>, and verifying that everything works as it should.</p>
<p>The major changes from GWeather-3.0 are:</p>
<ul>
<li>the <span class="caps">GTK3</span> widgets have gone to a farm up state, so you’ll have to write
your own <span class="caps">UI</span> for searching locations</li>
<li><code>GWeatherLocation</code> is a <code>GObject</code> type, so you can use it with
<code>GListModel</code> and friends, which should help with the point above</li>
<li>the deprecated <span class="caps">API</span> has been removed</li>
<li>the <span class="caps">API</span> that will be part of GWeather 4.0 will be stable, and regular
<span class="caps">API</span>/<span class="caps">ABI</span> stability guarantees will apply</li>
</ul>
<p>If you are using libgweather in your application, you should head over to
the <a href="https://gnome.pages.gitlab.gnome.org/libgweather/migrating-3to4.html">migration guide</a>
and check out what changed.</p>
<p>Ideally, there are still things that need to be cleaned up in the GWeather
<span class="caps">API</span>, for instance:</p>
<ul>
<li><code>GWeatherTimezone</code> parses the <code>tzdata</code> file directly, which comes with its
own set of issues, like having to track whether the time zone database has
changed or not; we should use
<a href="https://docs.gtk.org/glib/struct.TimeZone.html"><code>GTimeZone</code></a> instead, but
the <span class="caps">API</span> provided by the two types do not match entirely. I need to check
the current users of that <span class="caps">API</span>, and if possible, just drop the whole type.</li>
<li><code>GWeatherInfo</code> has a bunch of getter functions that return the bare values
and that can fail, and additional getter functions that always return a
formatted string, and cannot fail; it’s not a great <span class="caps">API</span>.</li>
<li><code>GWeatherLocation</code> returns the localised names by default, and has
additional getters for the “English” (really: <span class="caps">POSIX</span> C locale) names.</li>
</ul>
<p>If you encounter issues when porting, please: <a href="https://gitlab.gnome.org/GNOME/libgweather/issues/new">file an issue on
GitLab</a>.</p>GWeather next2021-10-15T10:52:00+01:002021-10-15T10:54:38+01:00ebassitag:www.bassi.io,2021-10-15:/articles/2021/10/15/gweather-next/<p>In which I explain what is happening in the libgweather repository</p><p><strong>tl;dr</strong> Libgweather, the small <span class="caps">GNOME</span> library that queries weather
services, is getting a major version bump to allow applications using it to
be ported to <span class="caps">GTK4</span>.</p>
<p><a href="https://gitlab.gnome.org/GNOME/gnome-applets/-/commit/8e29cc8ad6a0213100249be1b4a8643bd7ec1c1f">In the beginning</a>, there was a weather applet in the <span class="caps">GNOME</span>
panel. It had a bunch of code that poked at a couple of websites to get the
<a href="https://en.wikipedia.org/wiki/METAR">weather information</a> for a given airport or weather observation
stations, and shipped with a list of locations and their nearest <span class="caps">METAR</span> code.</p>
<p>In 2007, the relevant code was moved to <a href="https://gitlab.gnome.org/GNOME/libgweather/-/commit/32e0353d86ffc2df26b19608a680b19cfa3cc04f">its own separate
repository</a>, so that other applications and system
settings could reuse the same code as the panel applet: the libgweather
library was born. Aside from the basic weather information and location
objects, libgweather also had a couple of widgets: one for selecting a
location (with autocompletion), and one for selecting a timezone using a location.</p>
<p>Since libgweather was still very much an <em>ad hoc</em> library for a handful of
applications, there was no explicit <span class="caps">API</span> and <span class="caps">ABI</span> stability guarantee made by
its maintainers; in fact, in order to use it, you had to “opt in” with a
specific <a href="https://gitlab.gnome.org/GNOME/libgweather/-/commit/3ee7dec78f43735f9942b874b8bf707022c6a58d">C pre-processor symbol</a>.</p>
<p>Time passed, and a few more applications appeared during the initial <span class="caps">GNOME</span> 3
cycles—like <a href="https://gitlab.gnome.org/GNOME/gnome-weather">Weather</a>, followed by <a href="https://gitlab.gnome.org/GNOME/gnome-clocks">Clocks</a> a
month later. Most of the consumers of libgweather were actually going
through a language binding, which meant they were not really “opting into”
the <span class="caps">API</span> through the explicit pre-processor symbol; it also meant that
changes in the <span class="caps">API</span> and <span class="caps">ABI</span> could end up being found only <em>after</em> a
libgweather release, instead of during a development cycle. Of course, back
then, we only had a single <span class="caps">CI</span>/<span class="caps">CD</span> pipeline for the whole project, with far
too little granularity and far too wide scope. Still, the GWeather consumers
were few and far between, and the <span class="caps">API</span> was not stabilised.</p>
<p>Fast forward to now.</p>
<p>The core <span class="caps">GNOME</span> applications using GWeather are in the process of being
<a href="https://gitlab.gnome.org/GNOME/Initiatives/-/issues/26">ported to <span class="caps">GTK4</span></a>, but GWeather still ships with two <span class="caps">GTK3</span> widgets.
Since you cannot have <span class="caps">GTK3</span> and <span class="caps">GTK4</span> types in the same process, this requires
either porting GWeather to <span class="caps">GTK4</span> <em>or</em> dropping the widgets. As it turns out,
the widgets are not really shared across applications using libgweather, and
all of them have also been redesigned or are using the libadwaita/<span class="caps">GTK4</span> port
as a chance to refresh their overall appearences. This makes our life a
little bit easier, as we can drop the widgets without really losing any
actual functionality that people do care about.</p>
<p>For <span class="caps">GNOME</span> 42, the plan for libgweather is:</p>
<ul>
<li>bump up the <span class="caps">API</span> version to 4.0, and ensure parallel installability with
the older libgweather-3; this requires renaming things like the pkg-config
file and the settings schema, alongside the shared library</li>
<li>drop the <span class="caps">GTK</span> widgets, and some old <span class="caps">API</span> that hasn’t been working in years,
like getting the radar image animation</li>
<li>stabilise the <span class="caps">API</span>, and turn libgweather into a proper library, with the
usual <span class="caps">API</span> and <span class="caps">ABI</span> stability guarantees (deprecations and new symbols added
only during development cycles, no changes/removals until the following
major <span class="caps">API</span> bump)</li>
<li>make it easier to use libgweather objects with
<a href="https://docs.gtk.org/gio/iface.ListModel.html"><code>GListModel</code></a>-based <span class="caps">API</span></li>
<li>document the <span class="caps">API</span> properly</li>
<li>clean up the internals from various years of inconsistent coding style and practices</li>
</ul>
<p>I’m also going through the issues imported from Bugzilla and closing the
ones that have long since been fixed.</p>
<p>In the meantime, the old libgweather-3 <span class="caps">API</span> is going to be frozen, for the
tools that still use it and won’t be ported to <span class="caps">GTK4</span> any time soon.</p>
<p>For more information, you can read:</p>
<ul>
<li><a href="https://discourse.gnome.org/t/changes-in-libgweather-for-gnome-42/7770">the announcement topic on Discourse</a></li>
<li><a href="https://gitlab.gnome.org/GNOME/libgweather/-/issues/151">issue #151 on GitLab</a></li>
</ul>
<p>If you’re using libgweather, I strongly recommend you to use the 40.0
release or build from the <a href="https://gitlab.gnome.org/GNOME/libgweather/-/tree/libgweather-3">libgweather-3 branch</a> until you
are planning to port to <span class="caps">GTK4</span>.</p>
<p>If you’re distributing libgweather, I recommend you package the new
libgweather under a new name, given that it’s parallel installable with the
old one; my recommendation is to use <code>libgweather4</code> or <code>libgweather-4</code> as
the name of the package.</p>Properties, introspection, and you2021-09-21T21:22:00+01:002021-09-21T21:23:18+01:00ebassitag:www.bassi.io,2021-09-21:/articles/2021/09/21/properties-introspection-and-you/<p>In which I explain some changes in the GObject introspection format that landed in <span class="caps">GNOME</span> 41</p><p>It is a truth universally acknowledged, that a GObject class in possession of a property, must be in want of an accessor function.</p>
<p>The main issue with that statement is that it’s really hard to pair the GObject property with the accessor functions that set the property’s value, and retrieve it.</p>
<p>From a documentation perspective, tools might not establish any relation (gtk-doc), or they might require some additional annotation to do so (gi-docgen); but at the introspection level there’s nothing in the <span class="caps">XML</span> or the binary data that lets you go from a property name to a setter, or a getter, function. At least, until now.</p>
<p>GObject-introspection 1.70, released alongside GLib 2.70 and <span class="caps">GNOME</span> 41, introduced various annotations for both properties and methods that let you go from one to the other; additionally, new <span class="caps">API</span> was added to libgirepository to allow bindings to dynamic languages to establish that relation at run time.</p>
<h3>Annotations</h3>
<p>If you have a property, and you document it as you should, you’ll have something like this:</p>
<div class="highlight"><pre><span></span><code><span class="cm">/**</span>
<span class="cm"> * YourWidget:your-property</span>
<span class="cm"> *</span>
<span class="cm"> * A property that does something amazing.</span>
<span class="cm"> */</span>
</code></pre></div>
<p>If you want to associate the setter and getter functions to this property, all you need to do is add the following identifier annotations to it:</p>
<div class="highlight"><pre><span></span><code><span class="cm">/**</span>
<span class="cm"> * YourWidget:your-property: (setter set_your_property) (getter get_your_property)</span>
<span class="cm"> *</span>
<span class="cm"> * A property that does something amazing.</span>
<span class="cm"> */</span>
</code></pre></div>
<p>The <code>(setter)</code> and <code>(getter)</code> annotations take the name of the <strong>method</strong> that is used to set, and get, the property, respectively. The method name is relative to the type, so you should not pass the C symbol.</p>
<p>On the accessor methods side, you have two additional annotations:</p>
<div class="highlight"><pre><span></span><code><span class="cm">/**</span>
<span class="cm"> * your_widget_set_your_property: (set-property your-property)</span>
<span class="cm"> * @self: your widget</span>
<span class="cm"> * @value: the value to set</span>
<span class="cm"> *</span>
<span class="cm"> * Sets the given value for your property.</span>
<span class="cm"> */</span>
</code></pre></div>
<p>and:</p>
<div class="highlight"><pre><span></span><code><span class="cm">/**</span>
<span class="cm"> * your_widget_get_your_property: (get-property your-property)</span>
<span class="cm"> * @self: your widget</span>
<span class="cm"> *</span>
<span class="cm"> * Retrieves the value of your property.</span>
<span class="cm"> *</span>
<span class="cm"> * Returns: the value of the property</span>
<span class="cm"> */</span>
</code></pre></div>
<h3>Heuristics</h3>
<p>Of course, you’re now tempted to go and add those annotations to all your properties and related accessors. Before you do that, though, you should know that the introspection scanner will try and match properties and accessors by itself, using appropriate heuristics:</p>
<ul>
<li>if your object type has a writable, non-construct-only property, and a method that is called <code>set_<property></code>, then the property will have a setter and the method will be matched to the property</li>
<li>if your object type has a readable property, and a method that is called <code>get_<property></code>, then the property will have a getter and the method will be matched to the property</li>
<li>additionally, if the property is read-only and the property type is boolean, the scanner will look at a method that has the same name as the property as well; this is meant to catch getters like <code>gtk_widget_has_focus()</code>, which accesses the read-only property <code>has-focus</code></li>
</ul>
<h3><span class="caps">API</span></h3>
<p>All of the above ends up in the introspection <span class="caps">XML</span>, which is used by documentation tools and code generators. Bindings for dynamic languages using <a href="https://gnome.pages.gitlab.gnome.org/gobject-introspection/girepository/">libgirepository</a> can also access this information at run time, by using the <span class="caps">API</span> in <a href="https://gnome.pages.gitlab.gnome.org/gobject-introspection/girepository/gi-GIPropertyInfo.html"><code>GIPropertyInfo</code></a> to retrieve the setter and getter function information for a property; and the <span class="caps">API</span> in <a href="https://gnome.pages.gitlab.gnome.org/gobject-introspection/girepository/gi-GIFunctionInfo.html#g-function-info-get-property"><code>GIFunctionInfo</code></a> to retrieve the property being set.</p>
<h3>Future</h3>
<p>Ideally, with this information, language bindings should be able to call the accessor functions instead of going through the generic <code>g_object_set_property()</code> and <code>g_object_get_property()</code> <span class="caps">API</span>, except as a fallback. This should speed up the property access in various cases. Additionally, bindings could decide to stop exposing C accessors, and only expose the property, in order to make the <span class="caps">API</span> more idiomatic.</p>
<p>On the documentation side, this will ensure that tools like gi-docgen will be able to bind the properties and their accessors more reliably, without requiring <a href="https://gnome.pages.gitlab.gnome.org/gi-docgen/attributes.html">extra attributes</a>.</p>
<h3>And one more thing</h3>
<p>One thing that did not make it in time for the 1.70 release, but will land early in the next development cycle for gobject-introspection, is the validation for properties. Language bindings don’t really like it when the C <span class="caps">API</span> exposes properties that have the same name of methods and virtual functions; we <a href="https://gitlab.gnome.org/GNOME/gobject-introspection/-/merge_requests/277">already have a validation pass ready to land</a>, so expect warnings in the near future.</p>
<p>Another feature that will land early in the cycle is the <code>(emitter)</code> annotation, which will bind a method emitting a signal with the signal name. This is a feature taken from Vala’s metadata, and should improve the quality of life of people using introspection data with Vala, as well as removing the need for another attribute in gi-docgen.</p>
<p>Finally, if you maintain a language binding: <strong>please</strong> look at <a href="https://gitlab.gnome.org/GNOME/gobject-introspection/-/merge_requests/204">!204</a>, and make sure you’re not calling <code>g_assert_not_reached()</code> or <code>g_error()</code> when encountering a new scope type. The <code>forever</code> scope cannot land if it breaks every single binding in existence.</p>Publishing your documentation2021-08-26T00:51:00+01:002021-08-26T01:01:43+01:00ebassitag:www.bassi.io,2021-08-26:/articles/2021/08/26/publishing-your-documentation/<p>In which I explain how to build and publish your library’s <span class="caps">API</span> reference on GitLab</p><p>The main function of <a href="https://gitlab.gnome.org/Infrastructure/library-web">library-web</a>, the tool that published the
<span class="caps">API</span> reference of the various <span class="caps">GNOME</span> libraries, was to take release archives
and put their contents in a location that would be visible to a web server.
In 2006, this was the apex of automation, of course. These days? Not so much.</p>
<p>Since library-web is going the way of the Dodo, and we <em>do</em> have better ways
to automate the build and publishing of files with GitLab, how do we replace
library-web in 2021? The answer is, unsurprisingly: <a href="https://docs.gitlab.com/ee/ci/pipelines/">continuous integration
pipelines</a>.</p>
<p>I will assume that you’re already building—and testing—your library using
GitLab’s <span class="caps">CI</span>; if you aren’t, then you have bigger problems than just
publishing your <span class="caps">API</span>.</p>
<p>So, let’s start with these preconditions:</p>
<ul>
<li>your project is hosted on <a href="https://gitlab.gnome.org"><span class="caps">GNOME</span>’s GitLab instance</a></li>
<li>your project is using <a href="https://mesonbuild.com">Meson</a></li>
<li>your project is using <a href="https://gitlab.gnome.org/GNOME/gtk-doc/">gtk-doc</a> or <a href="https://gitlab.gnome.org/GNOME/gi-docgen/">gi-docgen</a></li>
</ul>
<p>If your project doesn’t satisfy these preconditions you might want to work
on doing so; alternatively, you can implement your own <span class="caps">CI</span> pipeline.</p>
<p>Let’s start with a simple job template:</p>
<div class="highlight"><pre><span></span><code><span class="c1"># Expected variables:</span>
<span class="c1"># PROJECT_DEPS: the dependencies for your own project</span>
<span class="c1"># MESON_VERSION: the version of Meson you depend on</span>
<span class="c1"># MESON_EXTRA_FLAGS: additional Meson setup options</span>
<span class="c1"># you wish to pass to the configuration phase</span>
<span class="c1"># DOCS_FLAGS: the Meson setup option for enabling the</span>
<span class="c1"># documentation, if any</span>
<span class="c1"># DOCS_PATH: the path of the generated reference,</span>
<span class="c1"># relative to the build root</span>
<span class="nt">.gidocgen-build</span><span class="p">:</span>
<span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">fedora:latest</span>
<span class="w"> </span><span class="nt">before_script</span><span class="p">:</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">export PATH="$HOME/.local/bin:$PATH"</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="p p-Indicator">></span>
<span class="w"> </span><span class="no">dnf install -y</span>
<span class="w"> </span><span class="no">python3</span>
<span class="w"> </span><span class="no">python3-pip</span>
<span class="w"> </span><span class="no">python3-wheel</span>
<span class="w"> </span><span class="no">gobject-introspection-devel</span>
<span class="w"> </span><span class="no">graphviz</span>
<span class="w"> </span><span class="no">ninja-build</span>
<span class="w"> </span><span class="no">redhat-rpm-config</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">dnf install -y ${PROJECT_DEPS}</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="p p-Indicator">></span>
<span class="w"> </span><span class="no">pip3 install</span>
<span class="w"> </span><span class="no">meson==${MESON_VERSION}</span>
<span class="w"> </span><span class="no">gi-docgen</span>
<span class="w"> </span><span class="no">jinja2</span>
<span class="w"> </span><span class="no">Markdown</span>
<span class="w"> </span><span class="no">markupsafe</span>
<span class="w"> </span><span class="no">pygments</span>
<span class="w"> </span><span class="no">toml</span>
<span class="w"> </span><span class="no">typogrify</span>
<span class="w"> </span><span class="nt">script</span><span class="p">:</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">meson setup ${MESON_EXTRA_FLAGS} ${DOCS_FLAGS} _docs .</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">meson compile -C _docs</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="p p-Indicator">|</span>
<span class="w"> </span><span class="no">pushd "_docs/${DOCS_PATH}" > /dev/null </span>
<span class="w"> </span><span class="no">tar cfJ ${CI_PROJECT_NAME}-docs.tar.xz .</span>
<span class="w"> </span><span class="no">popd > /dev/null</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">mv _docs/${DOCS_PATH}/${CI_PROJECT_NAME}-docs.tar .</span>
<span class="w"> </span><span class="nt">artifacts</span><span class="p">:</span>
<span class="w"> </span><span class="nt">when</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">always</span>
<span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="s">'Documentation'</span>
<span class="w"> </span><span class="nt">expose_as</span><span class="p">:</span><span class="w"> </span><span class="s">'Download</span><span class="nv"> </span><span class="s">the</span><span class="nv"> </span><span class="s">API</span><span class="nv"> </span><span class="s">reference'</span>
<span class="w"> </span><span class="nt">paths</span><span class="p">:</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">${CI_PROJECT_NAME}-docs.tar.xz</span>
</code></pre></div>
<p>This <span class="caps">CI</span> template will:</p>
<ul>
<li>download all the required dependencies for building the <span class="caps">API</span> reference
using gi-docgen</li>
<li>build your project, including the <span class="caps">API</span> reference</li>
<li>create an archive with the <span class="caps">API</span> reference</li>
<li>store the archive as a <span class="caps">CI</span> artefact that you can easily download</li>
</ul>
<p>Incidentally, by adding a <code>meson test -C _build</code> to the <code>script</code> section,
you can easily test your build as well; and if you have a <code>test()</code> target in
your build that runs <a href="https://gnome.pages.gitlab.gnome.org/gi-docgen/tools/check.html"><code>gi-docgen check</code></a>, then you can
verify that your documentation is always complete.</p>
<p>Now, all you have to do is create your own <span class="caps">CI</span> job that inherits from the
template inside its own stage. I will use <span class="caps">JSON</span>-GLib as a reference:</p>
<div class="highlight"><pre><span></span><code><span class="nt">stages</span><span class="p">:</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">docs</span>
<span class="nt">api-reference</span><span class="p">:</span>
<span class="w"> </span><span class="nt">stage</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">docs</span>
<span class="w"> </span><span class="nt">extends</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">.gidocgen-build</span>
<span class="w"> </span><span class="nt">needs</span><span class="p">:</span><span class="w"> </span><span class="p p-Indicator">[]</span>
<span class="w"> </span><span class="nt">variables</span><span class="p">:</span>
<span class="w"> </span><span class="nt">MESON_VERSION</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">0.55.3</span>
<span class="w"> </span><span class="nt">DOCS_FLAGS</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">-Dgtk_doc=true</span>
<span class="w"> </span><span class="nt">PROJECT_DEPS</span><span class="p">:</span>
<span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">gcc</span>
<span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">git</span>
<span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">gettext</span>
<span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">glib2-devel</span>
<span class="w"> </span><span class="nt">DOCS_PATH</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">docs/json-glib-1.0</span>
</code></pre></div>
<p><span class="dquo">“</span>What about gtk-doc!”, I hear from the back of the room. Well, fear not,
because there’s a similar template you can use if you’re still using gtk-doc
in your project:</p>
<div class="highlight"><pre><span></span><code><span class="c1"># Expected variables:</span>
<span class="c1"># PROJECT_DEPS: the dependencies for your own project</span>
<span class="c1"># MESON_VERSION: the version of Meson you depend on</span>
<span class="c1"># MESON_EXTRA_FLAGS: additional Meson setup options you</span>
<span class="c1"># wish to pass to the configuration phase</span>
<span class="c1"># DOCS_FLAGS: the Meson setup option for enabling the</span>
<span class="c1"># documentation, if any</span>
<span class="c1"># DOCS_TARGET: the Meson target for building the</span>
<span class="c1"># documentation, if any</span>
<span class="c1"># DOCS_PATH: the path of the generated reference,</span>
<span class="c1"># relative to the build root</span>
<span class="nt">.gtkdoc-build</span><span class="p">:</span>
<span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">fedora:latest</span>
<span class="w"> </span><span class="nt">before_script</span><span class="p">:</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">export PATH="$HOME/.local/bin:$PATH"</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="p p-Indicator">></span>
<span class="w"> </span><span class="no">dnf install -y</span>
<span class="w"> </span><span class="no">python3</span>
<span class="w"> </span><span class="no">python3-pip</span>
<span class="w"> </span><span class="no">python3-wheel</span>
<span class="w"> </span><span class="no">gtk-doc</span>
<span class="w"> </span><span class="no">ninja-build</span>
<span class="w"> </span><span class="no">redhat-rpm-config</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">dnf install -y ${PROJECT_DEPS}</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">pip3 install meson==${MESON_VERSION}</span>
<span class="w"> </span><span class="nt">script</span><span class="p">:</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">meson setup ${MESON_EXTRA_FLAGS} ${DOCS_FLAGS} _docs .</span>
<span class="w"> </span><span class="c1"># This is exceedingly annoying, but sadly its how</span>
<span class="w"> </span><span class="c1"># gtk-doc works in Meson</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">ninja -C _docs ${DOCS_TARGET}</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="p p-Indicator">|</span>
<span class="w"> </span><span class="no">pushd "_docs/${DOCS_PATH}" > /dev/null </span>
<span class="w"> </span><span class="no">tar cfJ ${CI_PROJECT_NAME}-docs.tar.xz .</span>
<span class="w"> </span><span class="no">popd > /dev/null</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">mv _docs/${DOCS_PATH}/${CI_PROJECT_NAME}-docs.tar .</span>
<span class="w"> </span><span class="nt">artifacts</span><span class="p">:</span>
<span class="w"> </span><span class="nt">when</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">always</span>
<span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="s">'Documentation'</span>
<span class="w"> </span><span class="nt">expose_as</span><span class="p">:</span><span class="w"> </span><span class="s">'Download</span><span class="nv"> </span><span class="s">the</span><span class="nv"> </span><span class="s">API</span><span class="nv"> </span><span class="s">reference'</span>
<span class="w"> </span><span class="nt">paths</span><span class="p">:</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">${CI_PROJECT_NAME}-docs.tar.xz</span>
</code></pre></div>
<p>And now you can use <code>extends: .gtkdoc-build</code> in your <code>api-reference</code> job.</p>
<p>Of course, this is just half of the job: the actual goal is to publish the
documentation using <a href="https://docs.gitlab.com/ee/user/project/pages/">GitLab’s Pages</a>. For that, you will need
another <span class="caps">CI</span> job in your pipeline, this time using the <code>deploy</code> stage:</p>
<div class="highlight"><pre><span></span><code><span class="nt">stages</span><span class="p">:</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">docs</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">deploy</span>
<span class="c1"># ... the api-reference job goes here...</span>
<span class="nt">pages</span><span class="p">:</span>
<span class="w"> </span><span class="nt">stage</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">deploy</span>
<span class="w"> </span><span class="nt">needs</span><span class="p">:</span><span class="w"> </span><span class="p p-Indicator">[</span><span class="s">'api-reference'</span><span class="p p-Indicator">]</span>
<span class="w"> </span><span class="nt">script</span><span class="p">:</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">mkdir public && cd public</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">tar xfJ ../${CI_PROJECT_NAME}-docs.tar.xz</span>
<span class="w"> </span><span class="nt">artifacts</span><span class="p">:</span>
<span class="w"> </span><span class="nt">paths</span><span class="p">:</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">public</span>
<span class="w"> </span><span class="nt">only</span><span class="p">:</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">master</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">main</span>
</code></pre></div>
<p>Now, once you push to your main development branch, your <span class="caps">API</span> reference will
be built by your <span class="caps">CI</span> pipeline, and the results published in your project’s
Pages space—<a href="https://gnome.pages.gitlab.gnome.org/json-glib/">like <span class="caps">JSON</span>-GLib</a>.</p>
<p>The <span class="caps">CI</span> pipeline and GitLab Pages are also useful for building complex,
static websites presenting multiple versions of the documentation; or
presenting multiple libraries. An example of the former is
<a href="https://gnome.pages.gitlab.gnome.org/libadwaita/">libadwaita’s website</a>, while an example of the latter is <a href="https://docs.gtk.org">the <span class="caps">GTK</span>
documentation website</a>. I’ll write a blog post about them another time.</p>
<hr>
<p>Given that the <span class="caps">CI</span> templates are pretty generic, I’m working on adding them
into the <a href="https://gitlab.gnome.org/GNOME/citemplates/-/merge_requests/29"><span class="caps">GNOME</span> ci-templates</a> repository, so you will be
able to use something like:</p>
<div class="highlight"><pre><span></span><code><span class="nt">include</span><span class="p">:</span><span class="w"> </span><span class="s">'https://gitlab.gnome.org/GNOME/citemplates/raw/HEAD/docs/gidocgen.yml'</span>
</code></pre></div>
<p>or:</p>
<div class="highlight"><pre><span></span><code><span class="nt">include</span><span class="p">:</span><span class="w"> </span><span class="s">'https://gitlab.gnome.org/GNOME/citemplates/raw/HEAD/docs/gtkdoc.yml'</span>
</code></pre></div>
<p>without having to copy-paste the template in your own <code>.gitlab-ci.yml</code> file.</p>
<hr>
<p>The obvious limitation of this approach is that you will need to depend on
the latest version of Fedora to build your project. Sadly, we cannot use
Flatpak and the <span class="caps">GNOME</span> run time images for this, mainly because we are
building libraries, not applications; and because extracting files out of a
Flatpak build after it has completed isn’t entirely trivial. Another side
effect is that if you bump up the dependencies of your project to something
on the bleeding edge and currently not packaged on the latest stable Fedora,
you will need to have it included as a Meson sub-project. Of course, you
should already be doing that, so it’s a minor downside.</p>
<p>Ideally, if <span class="caps">GNOME</span> built <a href="https://gitlab.gnome.org/GNOME/gnome-build-meta/-/issues/350">actual run time images for the <span class="caps">SDK</span></a>, we
could install gtk-doc, gi-docgen, and all their dependencies into the <span class="caps">SDK</span>
itself, and avoid depending on a real Linux distribution for the libraries
in our platform.</p>Documenting GNOME for developers2021-08-01T19:31:00+01:002021-08-02T10:32:04+01:00ebassitag:www.bassi.io,2021-08-01:/articles/2021/08/01/documenting-gnome-for-developers/<p>In which I outline what comes next in the developers documentation of the <span class="caps">GNOME</span> platform</p><p>You may have just now
<a href="https://discourse.gnome.org/t/new-gnome-developer-documentation-website/7134/1">noticed</a>
that the <a href="https://developer.gnome.org"><span class="caps">GNOME</span> developers documentation website has changed after 15
years</a>. You may also have noticed that it
contains drastically <em>less</em> content than it used to. Before you pick up
torches and pitchforks, let me give you a short <strong>tl;dr</strong> of the changes:</p>
<ul>
<li>Yes, this is entirely intentional</li>
<li>Yes, I know that stuff has been moved</li>
<li>Yes, I know that old URLs don’t work</li>
<li>Yes, some redirections will be put in place</li>
<li>No, we can’t go back</li>
</ul>
<hr>
<p>So let’s recap a bit the state of the developers documentation website in
2021, for those who weren’t in attendance at my <a href="https://gitlab.gnome.org/ebassi/guadec-2021/"><span class="caps">GUADEC</span> 2021
presentation</a>:</p>
<ul>
<li><a href="https://gitlab.gnome.org/Infrastructure/library-web">library-web</a> is a
Python application, which started as a Summer of Code project in 2006,
whose job was to take Autotools release tarballs, explode them, fiddle
with their contents, and then publish files on the gnome.org infrastructure.</li>
<li>library-web relies heavily on Autotools and gtk-doc.</li>
<li>library-web does a lot of pre-processing of the documentation to rewrite
links and <span class="caps">CSS</span> from the <span class="caps">HTML</span> files it receives.</li>
<li>library-web is very much a locally sourced, organic, artisanal pile of
hacks that revolve very much around the <span class="caps">GNOME</span> infrastructure from around 2007-2009.</li>
<li>library-web is incredibly hard to test locally, even when running inside
a container, and the logging is virtually non-existent.</li>
<li>library-web is still running on Python 2.</li>
<li>library-web is entirely unmaintained.</li>
</ul>
<p>That should cover the infrastructure side of things. Now let’s look at the content.</p>
<p>The developers documentation is divided in four sections:</p>
<ul>
<li>a platform overview</li>
<li>the Human Interface guidelines</li>
<li>guides and tutorials</li>
<li><span class="caps">API</span> references</li>
</ul>
<p>The platform overview is slightly out of date; the design team has been
reviewing the <span class="caps">HIG</span> and using <a href="https://gitlab.gnome.org/Teams/Design/hig-www">a new documentation
format</a>; the guides and
tutorials still like <span class="caps">GTK1</span> and <span class="caps">GTK2</span> content; or how to port <span class="caps">GNOME</span> 2
applications to <span class="caps">GNOME</span> 3; or how to write a Metacity theme.</p>
<p>This leaves us with the <span class="caps">API</span> references, which are a grab bag of
miscellaneous things, listed by version numbers. Outside of the C <span class="caps">API</span>
documentation, the only other references hosted on developer.gnome.org are
the C++ bindings—which, incidentally, use Doxygen and when they aren’t
broken by library-web messing about with the <span class="caps">HTML</span>, they have their own
franken-style mash up of gtkmm.org <em>and</em> developer.gnome.org.</p>
<h2>Why didn’t I know about this?</h2>
<p>If you’re asking this question, allow me to be blunt for a second: the
reason you never noticed that the developers documentation website was
broken is that you never actually experienced it for its intended use case.
Most likely, you either just looked in a couple of well known places and
never ventured outside of those; and/or you are a maintainer, and you never
literally cared how things worked (or didn’t work) after you uploaded a
release tarball somewhere. Like all infrastructure, it was somebody else’s problem.</p>
<p>I completely understand that we’re all volunteers, and that things that work
can be ignored because everyone has more important things to think about.</p>
<p>Sadly, things change: we don’t use Autotools (that much), which means
release archives do not contain the generated documentation any more; this
means library-web cannot be updated, unless somebody modifies the
configuration to look for a separate documentation tarball that the
maintainer has to generate manually <em>and</em> upload in a magic location on the
gnome.org file server—this has happened for <span class="caps">GTK4</span> and GLib for the past two years.</p>
<p>Projects change the way they lay out the documentation, or gtk-doc changes
something, and that causes library-web to stop extracting the right files;
you can look at <a href="https://developer-old.gnome.org/atk/2.30/">the <span class="caps">ATK</span>
reference</a> for the past year and
a half for an example.</p>
<p>Projects bump up their <span class="caps">API</span>, and now the cross-referencing gets broken, like
the <span class="caps">GTK3</span> pages linking <span class="caps">GDK2</span> types.</p>
<p>Finally, projects decide to change how their documentation is generated,
which means that library-web has no idea how to extract the <span class="caps">HTML</span> files, or
how to fiddle with them.</p>
<p>If you’re still using Autotools and gtk-doc, and haven’t done an <span class="caps">API</span> bump in
15 years, and all you care about is copying a release archive to the
gnome.org infrastructure I’m sure all of this will come as a surprise, and
I’m sorry you’re just now being confronted with a completely broken
infrastructure. Sadly, the infrastructure was broken for everybody else long
before this point.</p>
<h2>What did you do?</h2>
<p>I tried to make library-web deal with the changes in our infrastructure. I
personally built and uploaded multiple versions of the documentation for
GLib (three different archives for each release) for a year and a half; I
configured library-web to add more “extra tarball” locations for various
projects; I tried making library-web understand the new layout of various
projects; I even tried making library-web publish the gi-docgen references
used by <span class="caps">GTK</span>, Pango, and other projects.</p>
<p>Sadly, every change broke something else—and I’m not just talking about the
horrors of the code base. As library-web is responsible for determining the
structure of the documentation, any change to how the documentation is
handled leads to broken URLs, broken links, or broken redirections.</p>
<p>The entire castle of cards <em>needed to go</em>.</p>
<p>Which brings us to <a href="https://mail.gnome.org/archives/gnome-doc-list/2021-May/msg00000.html">the
plan</a>.</p>
<h2>What are you going to do?</h2>
<p>Well, the first step has been made: the new developer.gnome.org website does
not use library-web. The content has been refreshed, and more content is on
the way.</p>
<p>Again, this leaves the <span class="caps">API</span> references. For those, there are two things that
need to happen—and are planned for <span class="caps">GNOME</span> 41:</p>
<ol>
<li>all the libraries that are part of the <span class="caps">GNOME</span> <span class="caps">SDK</span> run time, built by
<a href="https://gitlab.gnome.org/GNOME/gnome-build-meta">gnome-build-meta</a> must
also build their documentation, which will be published as part of the
<code>org.gnome.Sdk.Docs</code> extension; the contents of the extension will also
be published online.</li>
<li>every library that is hosted on gnome.org infrastructure should publish
their documentation through their <span class="caps">CI</span> pipeline; for that, I’m working on
a <span class="caps">CI</span> template file and image that should take care of the easy projects,
and will act as model for projects that are more complicated.</li>
</ol>
<p>I’m happy to guide maintainers to deal with that, and I’m also happy to open
merge requests on various projects.</p>
<p>In the meantime, the old documentation is <a href="https://developer-old.gnome.org">still
available</a> as a static snapshot, and the
sysadmins are going to set up some redirections to bridge us from the old
platform to the new—and hopefully we’ll soon be able to redirect to each
project’s GitLab pages.</p>
<h2>Can we go back, please?</h2>
<p>Sadly, since nobody has ever bothered picking up the developers
documentation when it was still possible to incrementally fix it, going back
to a broken infrastructure isn’t going to help anybody.</p>
<p>We also cannot keep the old developer.gnome.org and add a new one, of
course; now we’d have two websites, one of which broken and unmaintained and
<em>linked all over the place</em>, and a new one that nobody knows exists.</p>
<p>The only way is forward, for better or worse.</p>
<h2>What about Devhelp</h2>
<p>Some of you may have noticed that I picked up the maintenance of
<a href="https://gitlab.gnome.org/GNOME/devhelp">Devhelp</a>, and landed a few fixes to
ensure that it can read the <span class="caps">GTK4</span> documentation. Outside of some visual
refresh for the <span class="caps">UI</span>, I also am working on making it load the contents of the
<code>org.gnome.Sdk.Docs</code> run time extension, which means it’ll be able to load
all the core <span class="caps">API</span> references. Ideally, we’re also going to see a port to <span class="caps">GTK4</span>
and libadwaita, as soon as WebKitGTK for <span class="caps">GTK4</span> is more wideley available.</p>Final Types2021-07-27T17:30:00+01:002021-07-28T11:02:30+01:00ebassitag:www.bassi.io,2021-07-27:/articles/2021/07/27/final-types/<p>In which I introduce a new GObject feature</p><p>The type system at the base of our platform, GType, has various kinds of derivability:</p>
<ul>
<li>simple derivability, where you’re allowed to create your derived version of an existing type, but you cannot derive your type any further;</li>
<li>deep derivability, where you’re allowed to derive types from other types;</li>
</ul>
<p>An example of the first kind is any type inheriting from <code>GBoxed</code>, whereas an example of the second kind is anything that inherits from <code>GTypeInstance</code>, like <code>GObject</code>.</p>
<p>Additionally, any derivable type can be marked as <em>abstract</em>; an abstract type cannot be instantiated, but you can create your own derived type which may or may not be “concrete”. Looking at the GType reference documentation, you’ll notice various macros and flags that exist to implement this functionality—including macros that were introduced to cut down the boilerplate necessary to declare and define new types.</p>
<p>The <code>G_DECLARE_*</code> family of macros, though, introduced a new concept in the type system: a “final” type. Final types are leaf nodes in the type hierarchy: they can be instantiated, but they cannot be derived any further. <span class="caps">GTK</span> 4 makes use of this kind of types to nudge developers towards composition, instead of inheritance. The main problem is that the concept of a “final” type is entirely orthogonal to the type system; there’s no way to programmatically know that a type is “final”—unless you have access to the introspection data and start playing with heuristics about symbol visibility. This means that language bindings are unable to know without human intervention if a type can actually be inherited from or not.</p>
<p>In GLib 2.70 we finally <a href="https://gitlab.gnome.org/GNOME/glib/-/issues/2321">plugged the hole in the type system</a>, and we introduced the <code>G_TYPE_FLAG_FINAL</code> flag. Types defined as “final” cannot be derived any further: as soon as you attempt to register your new type that inherits from a “final” type, you’ll get a warning at run time. There are macros available that will let you define final types, as well.</p>
<p>Thanks to the “final” flag, we can also include this information into <a href="https://gitlab.gnome.org/GNOME/gobject-introspection/-/merge_requests/257">the introspection data</a>; this will allow language bindings to warn you if you attempt at inheriting from a “final” type, likely using language-native tools, instead of getting a run time warning.</p>
<p>If you are using <code>G_DECLARE_FINAL_TYPE</code> in your code you should bump up your GObject dependency to 2.70, and switch your implementation from <code>G_DEFINE_TYPE</code> and friends to <code>G_DEFINE_FINAL_TYPE</code>.</p>More documentation changes2021-03-17T22:29:00+00:002021-10-07T00:54:06+01:00ebassitag:www.bassi.io,2021-03-17:/articles/2021/03/17/more-documentation-changes/<p>In which I report about the status of gi-docgen</p><p>It’s been nearly a month since I’ve talked about
<a href="https://gnome.pages.gitlab.gnome.org/gi-docgen/">gi-docgen</a>, my little tool to
generate <span class="caps">API</span> references from introspection data. In between <a href="https://www.bassi.io/articles/2021/02/19/documentation-changes/">my blog
post</a> and now, a few things have changed:</p>
<ul>
<li>the generated <span class="caps">API</span> reference has had a few improvements, most notably the
use of summaries in all index pages</li>
<li>all inheritable types now show the properties, signals, and methods
inherited from their ancestors and from the implemented interfaces; this
should hopefully make the reference much more useful for newcomers to
<span class="caps">GTK</span></li>
<li>we allow cross-linking between dependent namespaces; this is done using an
optional <span class="caps">URL</span> map, with links re-written on page load. Websites hosting
the <span class="caps">API</span> reference would need only to provide an <code>urlmap.js</code> file to
rewrite those links, instead of doing things like parsing the <span class="caps">HTML</span> and
changing the <code>href</code> attribute of every link <em>cough</em> library-web <em>cough</em></li>
<li>we parse <a href="https://gnome.pages.gitlab.gnome.org/gi-docgen/attributes.html">custom <span class="caps">GIR</span> attributes</a>
to provide better cross-linking between methods, properties, and signals.</li>
<li>we generate an index file with all the possible end-points, and a
dictionary of terms that can be used for searching; the terms are
stemmed using the <a href="https://en.wikipedia.org/wiki/Stemming">Porter stemming algorithm</a></li>
<li>the default template will let you search using the generated index; the
search supports scoping, so using <code>method:show widget</code> will look for all
the symbols in which the term <code>show</code> appears in method descriptions,
alongside the <code>widget</code> term</li>
<li>we also generate a DevHelp file, so theoretically DevHelp can load up the
<span class="caps">API</span> references built by gi-docgen; there is still <a href="https://gitlab.gnome.org/GNOME/devhelp/-/issues/28">work to be done</a>,
there, but thanks to the help of Jan Tojnar, it’s not entirely hopeless</li>
</ul>
<p>Thanks to all these changes, both Pango and <span class="caps">GTK</span> have switched from gtk-doc
to gi-docgen for their <span class="caps">API</span> references in their respective main development branches.</p>
<p>Now, here’s the part where it gets complicated.</p>
<h3>Using gi-docgen</h3>
<p>Quick reminder: the first and foremost use case for gi-docgen is <span class="caps">GTK</span> (and
<em>some</em> of its dependencies). If it works for you, I’m happy, but I will
<strong>not</strong> go out of my way to make your use case work—especially if it comes
at the expense of Job #1, i.e. generating the <span class="caps">API</span> reference for <span class="caps">GTK</span>.</p>
<p>Since gi-docgen is currently a slightly moving target, I <strong>strongly</strong>
recommend using it as a Meson subproject. I also <strong>strongly</strong> recommend
vendoring it inside your release tarballs, using:</p>
<div class="highlight"><pre><span></span><code><span class="nv">meson</span><span class="w"> </span><span class="nv">dist</span><span class="w"> </span><span class="o">--</span><span class="k">include</span><span class="o">-</span><span class="nv">subprojects</span>
</code></pre></div>
<p>when generating the distribution archive. Do <strong>not</strong> try and depend on an
installed copy of gi-docgen.</p>
<p>Additionally, it’s possible to include the gi-docgen <span class="caps">API</span>
reference into the Meson tarball by using <a href="https://gitlab.gnome.org/GNOME/pango/-/blob/master/build-aux/meson/dist-docs.py">a dist script</a>.
The <span class="caps">API</span> reference will be re-generated when building, but it can be
extracted from the tarball, like in the good old gtk-doc-on-Autotools days.</p>
<h3>Publishing your <span class="caps">API</span> reference</h3>
<p>The tool we use to generate developer.gnome.org,
<a href="https://gitlab.gnome.org/Infrastructure/library-web">library-web</a>, is
unmaintained and, quite frankly, fairly broken. It is a Python2 script that
got increasingly more complicated without actually getting more reliable; it
got progressively more broken once we started having more than two <span class="caps">GTK</span>
modules, and then it got severely broken once we started using Meson and
CMake, instead of Autotools. These days, you’ll be lucky to get your <span class="caps">API</span>
reference uploaded to developer.gnome.org (as a separate archive), and you
can definitely forget about cross-linking, because the tool will most likely
get things wrong in its quest to restyle any <span class="caps">HTML</span> it finds, and then fix the
references to what <em>it thinks</em> is the correct place:</p>
<ul>
<li><a href="https://gitlab.gnome.org/Infrastructure/library-web/-/issues/71">issue 71</a></li>
<li><a href="https://gitlab.gnome.org/Infrastructure/library-web/-/issues/95">issue 95</a></li>
<li><a href="https://gitlab.gnome.org/Infrastructure/library-web/-/issues/93">issue 93</a></li>
<li><a href="https://gitlab.gnome.org/Infrastructure/library-web/-/issues/92">issue 92</a></li>
<li><a href="https://gitlab.gnome.org/Infrastructure/library-web/-/issues/84">issue 84</a></li>
<li>and the list goes on and on…</li>
</ul>
<p>The support for Doxygen (which is used by the C++ bindings) is minimal, and
it ended up breaking a few times. Switching away from gtk-doc to gi-docgen
is basically the death knell for the whole thing:</p>
<ul>
<li>first of all, it cannot match the documentation module with the
configuration for it, because git-docgen does not have the concept of a
“documentation module”; at most, it has a project configuration file.</li>
<li>additionally, we <strong>really</strong> don’t want library-web messing about with the
generated <span class="caps">HTML</span>, especially if the end result breaks stuff.</li>
</ul>
<p>So, the current solution is to try and make library-web detect if we’re
using gi-docgen, by looking for <code>toml</code> and <code>toml.in</code> files in the release
archive, and then upload various files as they are. It’s a bad and fragile
stop gap solution, but it’s the best we can do without breaking everything
in even more terrible ways.</p>
<p>For <span class="caps">GNOME</span> 41 my plan is to sidestep the whole thing, and send library-web
to a farm upstate. We’re going to use gnome-build-meta to build the <span class="caps">API</span>
references of the projects we have in our <span class="caps">SDK</span>, and then publish them
according to the <span class="caps">SDK</span> version.</p>
<p>My recommendation for library authors, in any case, is to build the <span class="caps">API</span>
reference for the development branch of their project as part of their <span class="caps">CI</span>,
and then publish it to the GitLab pages space. For instance:</p>
<ul>
<li><a href="https://gnome.pages.gitlab.gnome.org/gtk/gtk4/"><span class="caps">GTK4</span></a></li>
<li><a href="https://gnome.pages.gitlab.gnome.org/pango/Pango/">Pango</a></li>
</ul>
<p>This way, you’ll always have access to the latest documentation.</p>
<p>Sadly, we can’t have per-branch references, because GitLab pages are nuked
every time a branch gets built; for that, we’d have to upload the artifacts
somewhere else, like an S3 bucket.</p>
<hr>
<p>Things are going to get better in the near future, after 10 years of
stagnation; sadly, this means we’re living in Interesting Times, so I ask of
you to please be patient while we transition towards a new and improved way
to document our platform.</p>Documentation changes2021-02-19T21:16:00+00:002021-02-21T01:34:59+00:00ebassitag:www.bassi.io,2021-02-19:/articles/2021/02/19/documentation-changes/<p>In which I talk about a new tool to generate the <span class="caps">API</span> reference for <span class="caps">GTK</span></p><p>Back in the late ‘90s, people working on <span class="caps">GTK</span> had the exact same problem we have
today: how do we document the collection of functions, types, macros, and
assorted symbols that we call “an <span class="caps">API</span>”. It’s all well and good to strive for
an <span class="caps">API</span> that can be immediately grasped by adhering to a set of well defined
conventions and naming; but nothing is, or really can be, “self documenting”.</p>
<p>When <span class="caps">GTK</span> 1.0 was released, the documentation was literally stored in
handwritten <a href="https://www.gnu.org/software/texinfo/">Texinfo files</a>; the <span class="caps">API</span>
footprint of <span class="caps">GTK</span> was small enough to still make it possible, but not really
maintainable in the longer term. In 1998, a new system was devised for
documenting <span class="caps">GTK</span> 1.2:</p>
<ul>
<li>a script, to parse the source files for the various declarations
and dump them into machine parseable “templates”, that would then
be modified to include the actual documentation, and committed to
the source repository</li>
<li>a small tool that would generate, compile, and run a small tool
to introspect the type system for things like hierarchy and signals</li>
<li>a script to take the templates, the list of symbols divided into
logical “sections”, an index file, and generate a bunch of DocBook
<span class="caps">XML</span> files</li>
<li>finally, a script to convert DocBook to <span class="caps">HTML</span> or man pages, via
<code>xsltproc</code> and an <span class="caps">XML</span> stylesheet</li>
</ul>
<p>Whenever somebody added a new symbol to <span class="caps">GTK</span>, they would need to run the
script, find the symbol in the template files, write the documentation using
DocBook tags if necessary, and then commit the changes alongside the rest of
the code.</p>
<p>Since this was 1998, and the scripts had to parse a bunch of text files
using regular expressions, they were written in Perl and strung together
with a bunch of Makefile rules.</p>
<p>Thus, <a href="https://gitlab.gnome.org/GNOME/gtk-doc">gtk-doc</a> was born.</p>
<p>Of course, since other libraries needed to provide an <span class="caps">API</span> reference to those
poor souls using them, gtk-doc ended up being shared across the <span class="caps">GNOME</span>
platform. We even built part of our website and release infrastructure
around it.</p>
<p>At some point between 1998 and 2009, gtk-doc gained the ability to generate
those template files incrementally, straight from the C sources; this
allowed moving the preambles of each section into the corresponding source
file, thus removing the templates from the repository, and keeping the
documentation close to the code it references, in the hope it would lead to
fewer instances of docs drift.</p>
<hr>
<p>Between 2009 and 2021, a few things happened:</p>
<ol>
<li><a href="https://gi.readthedocs.io/en/latest/">gobject-introspection</a> has become
“a thing”; g-i also parses the C code, and does it slightly more thoroughly
than gtk-doc, to gather the same information: declarations, hierarchy,
interfaces, properties, and signals, and even documentation, which is all
shoved into a well-defined <span class="caps">XML</span> file; on top of that, g-i needs annotations
in the source to produce a machine-readable description of the C <span class="caps">ABI</span> of a
library, which can then be used to generate language bindings</li>
<li>turns out that DocBook is pretty terrible, and running <code>xsltproc</code> on large,
complex DocBook files is <em>really slow</em></li>
<li>Perl isn’t really a Hot Language™ like it was in the late ‘90s; many
Linux distributions dropped it from the core installation, and not many
people speak it that fluently, which means not many people will want to
help with a large Perl application</li>
</ol>
<p>To cope with issue (1), gtk-doc had to learn to parse introspection annotations.</p>
<p>Issue (2) led to replacing DocBook tags inside the inline documentation with
subset of <a href="https://daringfireball.net/projects/markdown/">Markdown</a>,
augmented with custom code blocks and intra-document anchors for specific sections.</p>
<p>Issue (3) led to a wholesale rewrite in Python, in the hope that more people
would contribute to the maintenance of gtk-doc.</p>
<p>Sadly, all three solutions ended up breaking things in different ways:</p>
<ol>
<li>gtk-doc never really managed to express the introspection information in
the generated documentation, outside of references to an ancillary
appendix. If an annotation says “this argument can be <span class="caps">NULL</span>”, for
instance, there’s no need to write “or <span class="caps">NULL</span>” in the documentation
itself: the documentation tool can write it out for you.</li>
<li>the move to Markdown means that existing DocBook tags in the
documentation are now ignored or, worse, misinterpreted for <span class="caps">HTML</span> and
not rendered; this requires porting all the documentation in every
library, in a giant flag day, to avoid broken docs; on top of that,
DocBook’s style sheet to generate <span class="caps">HTML</span> started exhibiting regressions
after a build system change, which led, among other things, to the
disappearance of per-version symbols indices</li>
<li>the port to Python probably came too late, and ended up having many,
many regressions; gtk-doc is still a pretty complex tool, and it still
caters to many different use cases, spanning two decades; as much as its
use is documented and tested, its internals are really not, meaning that
it’s not an easy project to pick up</li>
</ol>
<p>Over the past 10 years various projects started migrating away from gtk-doc;
gobject-introspection itself shipped a documentation tool capable of
generating <span class="caps">API</span> references, though it mostly is a demonstrator of potential
capabilities more than an actual tool. Language bindings, on the other hand,
adopted the introspection data as the source for their documentation, and
you can see it in <a href="http://lazka.github.io/pgi-docs/">Python</a>,
<a href="https://gjs-docs.gnome.org/">JavaScript</a>, and
<a href="https://gtk-rs.org/docs/gtk/">Rust</a>.</p>
<p>As much as I’d like to contribute to gtk-doc, I’m afraid we reached the
point where we might want to experiment with something more radical,
instead of patching something up, and end up breaking what’s left.</p>
<p>So, since we’re starting from the bottom up, let’s figure out what are the
requirements for a tool to generate the documentation for <span class="caps">GTK</span>:</p>
<ul>
<li><em>be fast</em>. Building <span class="caps">GTK</span>’s <span class="caps">API</span> reference takes a long time. The <span class="caps">API</span>
footprint of <span class="caps">GDK</span>, <span class="caps">GSK</span>, and <span class="caps">GTK</span> is not small, but there’s no reason why
building the documentation should take a comparable amount of time
as building the library. We moved to Meson because it has improved the
build times of <span class="caps">GTK</span>, we don’t want to get into a bottleneck now.</li>
<li><em>no additional source parsing</em>. We already parse the C sources in order
to generate the introspection data, we don’t need another pass at that.</li>
<li><em>tailored for <span class="caps">GTK</span></em>. Whenever <span class="caps">GTK</span> changes, the tool must change with <span class="caps">GTK</span>;
the output must adapt to the style of documentation <span class="caps">GTK</span> uses.</li>
<li><em>integrated with <span class="caps">GTK</span></em>. We don’t want an external dependency that makes it
harder to deploy the <span class="caps">GTK</span> documentation on non-Linux platforms. Using it
as a sub-project would be the best option, followed by being able to
install it everywhere without additional, Linux-only dependencies.</li>
</ul>
<p>The explicit non-goal is to create a general purpose documentation tool. We
don’t need that; in fact: we’re actively avoiding it. Regardless of what
you’ve been taught at university, or your geeky instincts tell you, <em>not
every problem requires a generic solution</em>. The whole reason why we are in
this mess is that we took a tool for generating the <span class="caps">GTK</span> documentation and
then generalised the approach until it fell apart under its own weight.</p>
<p>If you want a general purpose documentation tool for C and C++ libraries,
there are many to choose from:</p>
<ul>
<li><a href="https://www.doxygen.nl/index.html">Doxygen</a></li>
<li><a href="https://hotdoc.github.io/">HotDoc</a></li>
<li><a href="https://casual-effects.com/markdeep/">MarkDeep</a></li>
</ul>
<p>There’s also gtk-doc: if you’re using it already, I strongly recommend
helping out with its maintenance.</p>
<hr>
<p>Back in November 2020, as a side project while we were closing in to the <span class="caps">GTK</span>
4.0 release date, <a href="https://gitlab.gnome.org/ebassi/gi-docgen">I started exploring the
idea</a> of parsing the
introspection data to generate the C <span class="caps">API</span> reference for <span class="caps">GTK</span>. I wanted to
start from scratch, and see how far I could go, so I deliberately avoided
taking the <span class="caps">GIR</span> parser from gobject-introspection; armed only with the <a href="https://gitlab.gnome.org/GNOME/gobject-introspection/-/blob/master/docs/gir-1.2.rnc"><span class="caps">GIR</span>
schema</a>
and a bunch of Python, I ended up writing a decent parser that would be able
to load the <span class="caps">GTK</span> introspection <span class="caps">XML</span> data, including its dependencies, and dump
the whole tree of C identifiers and symbols. After a break, at the end of
January 2021, I decided to take a page out the static website generator rule
book, and plugged the <a href="https://jinja.palletsprojects.com/en/2.11.x/">Jinja</a>
templates into the introspection data. The whole thing took about a couple
of weeks to go from this:</p>
<p><figure>
<figcaption class="image-caption">
<p>Everybody loves a tree-like output on the command line</p>
</figcaption>
<div><img src="https://www.bassi.io/images/gi-docgen-cli.png"/></div>
</figure></p>
<p>to this:</p>
<p><figure>
<figcaption class="image-caption">
<p>Behold! My stuff!</p>
</figcaption>
<div><img src="https://www.bassi.io/images/gi-docgen-web-old.png"/></div>
</figure></p>
<p>My evil plan of generating something decent enough to be usable and then
showing it to people with actual taste and web development skills paid off,
because I got a whole merge request from Martin Zilz to create a beautiful
theme, with support for responsive layout and even for a dark variant:</p>
<p><figure>
<figcaption class="image-caption">
<p>Amazing what actual taste and skill can accomplish</p>
</figcaption>
<div><img src="https://www.bassi.io/images/gi-docgen-web-new.png"/></div>
</figure></p>
<p><figure>
<figcaption class="image-caption">
<p>Like night and day</p>
</figcaption>
<div><img src="https://www.bassi.io/images/gi-docgen-web-light-dark.png"/></div>
</figure></p>
<p>Turns out that when you stop parsing C files and building small binaries to
introspect the type system, and remove DocBook and <code>xsltproc</code> from the
pipeline, things get fast. Who knew…</p>
<p>Additionally, once you move the template and the styling outside of the
generator, and you can create more complex documentation hierarchies, while
retaining the ability for people that are not programmers to change the
resulting <span class="caps">HTML</span>.</p>
<p>The interesting side effect of using introspection data is that our <span class="caps">API</span>
reference is now matching what language bindings are able to see and
consume—and oh boy, do <em>we</em> suck at that. Part of the fault lies in the
introspection parser not being able to cover some of the nastiest parts of
C, like macros—though, hopefully, that will improve in the near future; but
a lot of issues come from our own <span class="caps">API</span> design. Even after 10 years since the
introduction of introspection, we’re still doing some very dumb things when
it comes to C <span class="caps">API</span>—ad let’s ignore stuff that happened 20 years ago and that
we haven’t been able to fix yet. Hopefully, the documentation slapping us in
the face is going to help us figuring things out <em>before</em> they hit stable releases.</p>
<hr>
<p>What’s missing from gi-docgen? Well, you can look at the <a href="https://gitlab.gnome.org/ebassi/gi-docgen/-/milestones/1">2021.1 milestone
on GitLab</a>:</p>
<ul>
<li>more documentation on the ancillary files used for project and template
configuration; stabilising the key/value pairs would also be part of the
documentation effort</li>
<li>client-side search, with symbols exposed to tools like <span class="caps">GNOME</span> Builder
through something that isn’t quite as tragic as DevHelp files</li>
<li>automatic cross-linking with dependencies documented by gi-docgen,
especially for libraries with multiple namespaces, like <span class="caps">GTK</span> and Pango</li>
<li>generating proper dependency files, for the consumption of build
tools like Meson</li>
</ul>
<p>In the meantime, what’s missing for <span class="caps">GTK</span> to use this? Mainly, porting the
documentation away from the various gtk-doc-isms, like marking symbols with
sigils, or using <code>|[ ... ]|</code> to define code blocks. Additionally, since the
introspection scanner only attaches <code>SECTION</code> blocks to the documentation
element of a class, all the sections that operate as “grab bag of related
symbols” need to be moved to a separate Markdown file.</p>
<p><strong>It must needs be remarked</strong>: gi-docgen is <strong>not</strong> a generic solution for
documenting C libraries. If your library does not have introspection,
doesn’t use type classes, or has a very different <span class="caps">ABI</span> exposed through
introspection than the actual C <span class="caps">API</span>, then you’re not going to find it
useful—and I don’t have any plans to cater to your use cases either. You
should keep using gtk-doc if it still works for you; or you may want to
consider other documentation tools.</p>
<p>Anything that complicates the goal of this tool—generating the <span class="caps">API</span> reference
for <span class="caps">GTK</span> and ancillary libraries—is completely out of scope.</p>
<hr>
<ul>
<li><a href="https://gitlab.gnome.org/ebassi/gi-docgen">The <code>gi-docgen</code> repository</a></li>
<li><a href="https://people.gnome.org/~ebassi/docs/_build/Gtk/4.0/">A demo render of the <span class="caps">GTK</span> reference</a></li>
<li><a href="https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/3222">The <span class="caps">MR</span> for <span class="caps">GTK</span></a></li>
</ul>Type instances2020-06-02T16:06:00+01:002020-06-02T16:06:27+01:00ebassitag:www.bassi.io,2020-06-02:/articles/2020/06/02/type-instances/<p>In which we talk about derivable types that are not GObject</p><p><a href="https://www.amazon.co.uk/Neoreaction-Basilisk-Essays-Around-Alt-Right-ebook/dp/B0782JDGVQ/">Let us assume we are writing a library</a>.</p>
<p>The particular nature of our work is up for any amount of debate, but the
basic fact of it comes with a few requirements, and they are by and large
inevitable if you wish to be a well-behaved, well-integrated member of the
<span class="caps">GNOME</span> community. One of which is: “please, think of the language bindings”.
These days, luckily for all of us, this means writing introspectable
interfaces that adhere to fairly sensible best practices and conventions.</p>
<p>One of the basic conventions has to do with types. By and large, types
exposed by libraries fall into these two categories:</p>
<ul>
<li>plain old data structures, which are represented by what’s called a
“boxed” type; these are simple types with a copy and a free function,
mostly meant for marshalling things around so that language bindings can
implement properties, signal handlers, and abide to ownership transfer
rules. <em>Boxed types cannot have sub-types</em>.</li>
<li>object types, used for everything else: properties, emitting signals,
inheritance, interface implementation, the whole shebang.</li>
</ul>
<p>Boxed and object types cover most of the functionality in a modern,
GObject-based <span class="caps">API</span>, and people can consume the very same <span class="caps">API</span> from languages
that are not C.</p>
<p>Except that there’s a third, kind of niche data type:</p>
<ul>
<li>fully opaque, with instance fields only known within the scope of the
project itself</li>
<li>immutable, or at least with low-mutability, after construction</li>
<li>reference counted, with optional cloning and serialization</li>
<li>derivable within the scope of the project, typically with a base
abstract class</li>
<li>without signals or properties</li>
</ul>
<h3>Boxing</h3>
<p>One strategy used to implement this niche type has been to use a boxed type,
and then invent some private, ad hoc derivation technique, with some
structure containing function pointers used as a vtable, for instance:</p>
<figure class='code'>
<figcaption><span class="liquid-tags-code-filename">boxed-type.c</span><span class="liquid-tags-code-lines">[Lines 3-79]</span><a href='/code/types/boxed-type.c'>download</a></figcaption>
<div class="highlight"><pre><span></span><code><span class="cm">/* {{{ Base */</span>
<span class="k">typedef</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">_Base</span><span class="w"> </span><span class="n">Base</span><span class="p">;</span>
<span class="k">typedef</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">_BaseClass</span><span class="w"> </span><span class="n">BaseClass</span><span class="p">;</span>
<span class="k">struct</span><span class="w"> </span><span class="nc">_BaseClass</span>
<span class="p">{</span>
<span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">type_name</span><span class="p">;</span>
<span class="w"> </span><span class="n">gsize</span><span class="w"> </span><span class="n">instance_size</span><span class="p">;</span>
<span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="p">(</span><span class="o">*</span><span class="w"> </span><span class="n">finalize</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Base</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="p">);</span>
<span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="p">(</span><span class="o">*</span><span class="w"> </span><span class="n">foo</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Base</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="p">);</span>
<span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="p">(</span><span class="o">*</span><span class="w"> </span><span class="n">bar</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Base</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">struct</span><span class="w"> </span><span class="nc">_Base</span>
<span class="p">{</span>
<span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="n">BaseClass</span><span class="w"> </span><span class="o">*</span><span class="n">base_class</span><span class="p">;</span>
<span class="w"> </span><span class="c1">// Shared field</span>
<span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">some_field</span><span class="p">;</span>
<span class="p">};</span>
<span class="c1">// Allocate the instance described in the vtable</span>
<span class="k">static</span><span class="w"> </span><span class="n">Base</span><span class="w"> </span><span class="o">*</span>
<span class="nf">base_alloc</span><span class="w"> </span><span class="p">(</span><span class="k">const</span><span class="w"> </span><span class="n">BaseClass</span><span class="w"> </span><span class="o">*</span><span class="n">vtable</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="c1">// Simple check to ensure that the derived instance includes the</span>
<span class="w"> </span><span class="c1">// parent base type</span>
<span class="w"> </span><span class="n">g_assert</span><span class="w"> </span><span class="p">(</span><span class="n">vtable</span><span class="o">-></span><span class="n">instance_size</span><span class="w"> </span><span class="o">>=</span><span class="w"> </span><span class="k">sizeof</span><span class="w"> </span><span class="p">(</span><span class="n">Base</span><span class="p">));</span>
<span class="w"> </span><span class="c1">// Use the atomic refcounted boxing to allocated the requested</span>
<span class="w"> </span><span class="c1">// instance size</span>
<span class="w"> </span><span class="n">Base</span><span class="w"> </span><span class="o">*</span><span class="n">res</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">g_atomic_rc_box_new</span><span class="w"> </span><span class="p">(</span><span class="n">vtable</span><span class="o">-></span><span class="n">instance_size</span><span class="p">);</span>
<span class="w"> </span><span class="c1">// Store the vtable</span>
<span class="w"> </span><span class="n">res</span><span class="o">-></span><span class="n">base_class</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">vtable</span><span class="p">;</span>
<span class="w"> </span><span class="c1">// Initialize the base instance fields</span>
<span class="w"> </span><span class="n">res</span><span class="o">-></span><span class="n">some_field</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">42</span><span class="p">;</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">res</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">static</span><span class="w"> </span><span class="kt">void</span>
<span class="nf">base_finalize</span><span class="w"> </span><span class="p">(</span><span class="n">Base</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="c1">// Allow derived types to clear up their own instance data</span>
<span class="w"> </span><span class="n">self</span><span class="o">-></span><span class="n">base_class</span><span class="o">-></span><span class="n">finalize</span><span class="w"> </span><span class="p">(</span><span class="n">self</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">Base</span><span class="w"> </span><span class="o">*</span>
<span class="nf">base_ref</span><span class="w"> </span><span class="p">(</span><span class="n">Base</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">g_atomic_rc_box_acquire</span><span class="w"> </span><span class="p">(</span><span class="n">self</span><span class="p">);</span>
<span class="p">}</span>
<span class="kt">void</span>
<span class="nf">base_unref</span><span class="w"> </span><span class="p">(</span><span class="n">Base</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="n">g_atomic_rc_box_release</span><span class="w"> </span><span class="p">(</span><span class="n">self</span><span class="p">,</span><span class="w"> </span><span class="n">base_finalize</span><span class="p">);</span>
<span class="p">}</span>
<span class="kt">void</span>
<span class="nf">base_foo</span><span class="w"> </span><span class="p">(</span><span class="n">Base</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="n">self</span><span class="o">-></span><span class="n">base_class</span><span class="o">-></span><span class="n">foo</span><span class="w"> </span><span class="p">(</span><span class="n">self</span><span class="p">);</span>
<span class="p">}</span>
<span class="kt">void</span>
<span class="nf">base_bar</span><span class="w"> </span><span class="p">(</span><span class="n">Base</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="n">self</span><span class="o">-></span><span class="n">base_class</span><span class="o">-></span><span class="n">bar</span><span class="w"> </span><span class="p">(</span><span class="n">self</span><span class="p">);</span>
<span class="p">}</span>
<span class="c1">// Add a GType for the base type</span>
<span class="n">G_DEFINE_BOXED_TYPE</span><span class="w"> </span><span class="p">(</span><span class="n">Base</span><span class="p">,</span><span class="w"> </span><span class="n">base</span><span class="p">,</span><span class="w"> </span><span class="n">base_ref</span><span class="p">,</span><span class="w"> </span><span class="n">base_unref</span><span class="p">)</span>
<span class="cm">/* }}} */</span>
</code></pre></div>
</figure>
<p>The code above lets us create derived types that conform to the base type
<span class="caps">API</span> contract, while providing additional functionality; for instance:</p>
<figure class='code'>
<figcaption><span class="liquid-tags-code-filename">boxed-type.c</span><span class="liquid-tags-code-lines">[Lines 81-123]</span><a href='/code/types/boxed-type.c'>download</a></figcaption>
<div class="highlight"><pre><span></span><code><span class="cm">/* {{{ DerivedA */</span>
<span class="k">typedef</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">Base</span><span class="w"> </span><span class="n">parent</span><span class="p">;</span>
<span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">some_other_field</span><span class="p">;</span>
<span class="p">}</span><span class="w"> </span><span class="n">DerivedA</span><span class="p">;</span>
<span class="k">static</span><span class="w"> </span><span class="kt">void</span>
<span class="nf">derived_a_finalize</span><span class="w"> </span><span class="p">(</span><span class="n">Base</span><span class="w"> </span><span class="o">*</span><span class="n">base</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="n">DerivedA</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="n">DerivedA</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="w"> </span><span class="n">base</span><span class="p">;</span>
<span class="w"> </span><span class="n">g_free</span><span class="w"> </span><span class="p">(</span><span class="n">self</span><span class="o">-></span><span class="n">some_other_field</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">static</span><span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="n">BaseClass</span><span class="w"> </span><span class="n">derived_a_class</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="p">.</span><span class="n">type_name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">"DerivedA"</span><span class="p">,</span>
<span class="w"> </span><span class="p">.</span><span class="n">instance_size</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">sizeof</span><span class="w"> </span><span class="p">(</span><span class="n">DerivedA</span><span class="p">),</span>
<span class="w"> </span><span class="p">.</span><span class="n">finalize</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">derived_a_finalize</span><span class="p">,</span>
<span class="w"> </span><span class="p">.</span><span class="n">foo</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">derived_a_foo</span><span class="p">,</span><span class="w"> </span><span class="c1">// defined elsewhere</span>
<span class="w"> </span><span class="p">.</span><span class="n">bar</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">derived_a_bar</span><span class="p">,</span><span class="w"> </span><span class="c1">// defined elsewhere</span>
<span class="p">};</span>
<span class="n">Base</span><span class="w"> </span><span class="o">*</span>
<span class="nf">derived_a_new</span><span class="w"> </span><span class="p">(</span><span class="k">const</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">some_other_field</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="n">Base</span><span class="w"> </span><span class="o">*</span><span class="n">res</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">base_alloc</span><span class="w"> </span><span class="p">(</span><span class="o">&</span><span class="n">derived_a_class</span><span class="p">);</span>
<span class="w"> </span><span class="n">DerivedA</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="n">DerivedA</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="w"> </span><span class="n">res</span><span class="p">;</span>
<span class="w"> </span><span class="n">self</span><span class="o">-></span><span class="n">some_other_field</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">g_strdup</span><span class="w"> </span><span class="p">(</span><span class="n">some_other_field</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">res</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">const</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span>
<span class="nf">derived_a_get_some_other_field</span><span class="w"> </span><span class="p">(</span><span class="n">Base</span><span class="w"> </span><span class="o">*</span><span class="n">base</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="n">DerivedA</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="n">DerivedA</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="w"> </span><span class="n">base</span><span class="p">;</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">self</span><span class="o">-></span><span class="n">some_other_field</span><span class="p">;</span>
<span class="p">}</span>
<span class="cm">/* }}} */</span>
</code></pre></div>
</figure>
<p>Since the <code>Base</code> type is also a boxed type, it can be used for signal
marshallers and GObject properties at zero cost.</p>
<p>This whole thing seems pretty efficient, and fairly simple to wrap your head
around, but things fall apart pretty quickly as soon as you make this <span class="caps">API</span>
public and tell people to use it from languages that are not C.</p>
<p>As I said above, <em>boxed types cannot have sub-types</em>; the type system has no
idea that <code>DerivedA</code> implements the <code>Base</code> <span class="caps">API</span> contract. Additionally, since
the whole introspection system is based on conventions applied on top of
some C <span class="caps">API</span>, there is no way for language bindings to know that the
<code>derived_a_get_some_other_field()</code> function is really a <code>DerivedA</code> method,
meant to operate on <code>DerivedA</code> instances. Instead, you’ll only be able to
access the method as a static function, like:</p>
<div class="highlight"><pre><span></span><code><span class="n">obj</span> <span class="o">=</span> <span class="n">Namespace</span><span class="o">.</span><span class="n">derived_a_new</span><span class="p">()</span>
<span class="n">Namespace</span><span class="o">.</span><span class="n">derived_a_get_some_other_field</span><span class="p">(</span><span class="n">obj</span><span class="p">)</span>
</code></pre></div>
<p>instead of the idiomatic, and natural:</p>
<div class="highlight"><pre><span></span><code>obj = Namespace.DerivedA.new()
obj.get_some_other_field()
</code></pre></div>
<p>In short: please, don’t use boxed types for this, unless you’re planning to
hide this functionality from the public <span class="caps">API</span>.</p>
<h3>Typed instances</h3>
<p>At this point the recommendation would be to switch to GObject for your
type; make the type derivable in your project’s scope, avoid properties and
signals, and you get fairly idiomatic code, and a bunch of other features,
like weak references, toggle references, and keyed instance data. You can
use your types for properties and signals, and you’re pretty much done.</p>
<p>But <em>what if you don’t want to use GObject…</em></p>
<p>Well, in that case GLib lets you create your own type hierarchy, with its
own rules, by using <a href="https://developer.gnome.org/gobject/stable/gobject-Type-Information.html#GTypeInstance"><code>GTypeInstance</code></a> as the base type.</p>
<p><code>GTypeInstance</code> is the common ancestor for everything that is meant to be
derivable; it’s the base type for <code>GObject</code> as well. Implementing a
<code>GTypeInstance</code>-derived hierarchy doesn’t take much effort: it’s mostly low
level glue code:</p>
<figure class='code'>
<figcaption><span class="liquid-tags-code-filename">instance-type.c</span><span class="liquid-tags-code-lines">[Lines 4-189]</span><a href='/code/types/instance-type.c'>download</a></figcaption>
<div class="highlight"><pre><span></span><code><span class="k">typedef</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">_Base</span><span class="w"> </span><span class="n">Base</span><span class="p">;</span>
<span class="k">typedef</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">_BaseClass</span><span class="w"> </span><span class="n">BaseClass</span><span class="p">;</span>
<span class="k">typedef</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">_BaseTypeInfo</span><span class="w"> </span><span class="n">BaseTypeInfo</span><span class="p">;</span>
<span class="cp">#define BASE_TYPE (base_get_type())</span>
<span class="cp">#define BASE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), BASE_TYPE, </span>
<span class="c1">// Simple macro that lets you chain up to the parent type's implementation</span>
<span class="c1">// of a virtual function, e.g.:</span>
<span class="c1">//</span>
<span class="c1">// BASE_SUPER (self)->finalize (obj);</span>
<span class="cp">#define BASE_SUPER(obj) ((BaseClass *) g_type_class_peek (g_type_parent (G_TYPE_FROM_INSTANCE (obj))))</span>
<span class="k">struct</span><span class="w"> </span><span class="nc">_BaseClass</span>
<span class="p">{</span>
<span class="w"> </span><span class="n">GTypeClass</span><span class="w"> </span><span class="n">parent_class</span><span class="p">;</span>
<span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="p">(</span><span class="o">*</span><span class="w"> </span><span class="n">finalize</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Base</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="p">);</span>
<span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="p">(</span><span class="o">*</span><span class="w"> </span><span class="n">foo</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Base</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="p">);</span>
<span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="p">(</span><span class="o">*</span><span class="w"> </span><span class="n">bar</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Base</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">struct</span><span class="w"> </span><span class="nc">_Base</span>
<span class="p">{</span>
<span class="w"> </span><span class="n">GTypeInstance</span><span class="w"> </span><span class="n">parent_instance</span><span class="p">;</span>
<span class="w"> </span><span class="n">gatomicrefcount</span><span class="w"> </span><span class="n">ref_count</span><span class="p">;</span>
<span class="w"> </span><span class="c1">// Shared field</span>
<span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">some_field</span><span class="p">;</span>
<span class="p">};</span>
<span class="c1">// A structure to be filled out by derived types when registering</span>
<span class="c1">// themselves into the type system; it copies the vtable into the</span>
<span class="c1">// class structure, and defines the size of the instance</span>
<span class="k">struct</span><span class="w"> </span><span class="nc">_BaseTypeInfo</span>
<span class="p">{</span>
<span class="w"> </span><span class="n">gsize</span><span class="w"> </span><span class="n">instance_size</span><span class="p">;</span>
<span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="p">(</span><span class="o">*</span><span class="w"> </span><span class="n">finalize</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Base</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="p">);</span>
<span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="p">(</span><span class="o">*</span><span class="w"> </span><span class="n">foo</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Base</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="p">);</span>
<span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="p">(</span><span class="o">*</span><span class="w"> </span><span class="n">bar</span><span class="p">)</span><span class="w"> </span><span class="p">(</span><span class="n">Base</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="p">);</span>
<span class="p">};</span>
<span class="c1">// GValue table, so that you can initialize, compare, and clear</span>
<span class="c1">// your type inside a GValue, as well as collect/copy it when</span>
<span class="c1">// dealing with variadic arguments</span>
<span class="k">static</span><span class="w"> </span><span class="kt">void</span>
<span class="nf">value_base_init</span><span class="w"> </span><span class="p">(</span><span class="n">GValue</span><span class="w"> </span><span class="o">*</span><span class="n">value</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="n">value</span><span class="o">-></span><span class="n">data</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">v_pointer</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">NULL</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">static</span><span class="w"> </span><span class="kt">void</span>
<span class="nf">value_base_free_value</span><span class="w"> </span><span class="p">(</span><span class="n">GValue</span><span class="w"> </span><span class="o">*</span><span class="n">value</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">value</span><span class="o">-></span><span class="n">data</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">v_pointer</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="nb">NULL</span><span class="p">)</span>
<span class="w"> </span><span class="n">base_unref</span><span class="w"> </span><span class="p">(</span><span class="n">value</span><span class="o">-></span><span class="n">data</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">v_pointer</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">static</span><span class="w"> </span><span class="kt">void</span>
<span class="nf">value_base_copy_value</span><span class="w"> </span><span class="p">(</span><span class="k">const</span><span class="w"> </span><span class="n">GValue</span><span class="w"> </span><span class="o">*</span><span class="n">src</span><span class="p">,</span>
<span class="w"> </span><span class="n">GValue</span><span class="w"> </span><span class="o">*</span><span class="n">dst</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">src</span><span class="o">-></span><span class="n">data</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">v_pointer</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="nb">NULL</span><span class="p">)</span>
<span class="w"> </span><span class="n">dst</span><span class="o">-></span><span class="n">data</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">v_pointer</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">base_ref</span><span class="w"> </span><span class="p">(</span><span class="n">src</span><span class="o">-></span><span class="n">data</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">v_pointer</span><span class="p">);</span>
<span class="w"> </span><span class="k">else</span>
<span class="w"> </span><span class="n">dst</span><span class="o">-></span><span class="n">data</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">v_pointer</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">NULL</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">static</span><span class="w"> </span><span class="n">gpointer</span>
<span class="nf">value_expression_peek_pointer</span><span class="w"> </span><span class="p">(</span><span class="k">const</span><span class="w"> </span><span class="n">GValue</span><span class="w"> </span><span class="o">*</span><span class="n">value</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">value</span><span class="o">-></span><span class="n">data</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">v_pointer</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">static</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span>
<span class="nf">value_base_collect_value</span><span class="w"> </span><span class="p">(</span><span class="n">GValue</span><span class="w"> </span><span class="o">*</span><span class="n">value</span><span class="p">,</span>
<span class="w"> </span><span class="n">guint</span><span class="w"> </span><span class="n">n_collect_values</span><span class="p">,</span>
<span class="w"> </span><span class="n">GTypeCValue</span><span class="w"> </span><span class="o">*</span><span class="n">collect_values</span><span class="p">,</span>
<span class="w"> </span><span class="n">guint</span><span class="w"> </span><span class="n">collect_flags</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="n">Base</span><span class="w"> </span><span class="o">*</span><span class="n">base</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">collect_values</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">v_pointer</span><span class="p">;</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">base</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="nb">NULL</span><span class="p">)</span>
<span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">value</span><span class="o">-></span><span class="n">data</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">v_pointer</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">NULL</span><span class="p">;</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">NULL</span><span class="p">;</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">base</span><span class="o">-></span><span class="n">parent_instance</span><span class="p">.</span><span class="n">g_class</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="nb">NULL</span><span class="p">)</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">g_strconcat</span><span class="w"> </span><span class="p">(</span><span class="s">"invalid unclassed Base pointer for "</span>
<span class="w"> </span><span class="s">"value type '"</span><span class="p">,</span>
<span class="w"> </span><span class="n">G_VALUE_TYPE_NAME</span><span class="w"> </span><span class="p">(</span><span class="n">value</span><span class="p">),</span>
<span class="w"> </span><span class="s">"'"</span><span class="p">,</span>
<span class="w"> </span><span class="nb">NULL</span><span class="p">);</span>
<span class="w"> </span><span class="n">value</span><span class="o">-></span><span class="n">data</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">v_pointer</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">base_ref</span><span class="w"> </span><span class="p">(</span><span class="n">base</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">NULL</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">static</span><span class="w"> </span><span class="n">gchar</span><span class="w"> </span><span class="o">*</span>
<span class="nf">value_base_lcopy_value</span><span class="w"> </span><span class="p">(</span><span class="k">const</span><span class="w"> </span><span class="n">GValue</span><span class="w"> </span><span class="o">*</span><span class="n">value</span><span class="p">,</span>
<span class="w"> </span><span class="n">guint</span><span class="w"> </span><span class="n">n_collect_values</span><span class="p">,</span>
<span class="w"> </span><span class="n">GTypeCValue</span><span class="w"> </span><span class="o">*</span><span class="n">collect_values</span><span class="p">,</span>
<span class="w"> </span><span class="n">guint</span><span class="w"> </span><span class="n">collect_flags</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="n">Base</span><span class="w"> </span><span class="o">**</span><span class="n">base_p</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">collect_values</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">v_pointer</span><span class="p">;</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">base_p</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="nb">NULL</span><span class="p">)</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">g_strconcat</span><span class="w"> </span><span class="p">(</span><span class="s">"value location for '"</span><span class="p">,</span>
<span class="w"> </span><span class="n">G_VALUE_TYPE_NAME</span><span class="w"> </span><span class="p">(</span><span class="n">value</span><span class="p">),</span>
<span class="w"> </span><span class="s">"' passed as NULL"</span><span class="p">,</span>
<span class="w"> </span><span class="nb">NULL</span><span class="p">);</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">value</span><span class="o">-></span><span class="n">data</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">v_pointer</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="nb">NULL</span><span class="p">)</span>
<span class="w"> </span><span class="o">*</span><span class="n">base_p</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">NULL</span><span class="p">;</span>
<span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">collect_flags</span><span class="w"> </span><span class="o">&</span><span class="w"> </span><span class="n">G_VALUE_NOCOPY_CONTENTS</span><span class="p">)</span>
<span class="w"> </span><span class="o">*</span><span class="n">base_p</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">value</span><span class="o">-></span><span class="n">data</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">v_pointer</span><span class="p">;</span>
<span class="w"> </span><span class="k">else</span>
<span class="w"> </span><span class="o">*</span><span class="n">base_p</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">base_ref</span><span class="w"> </span><span class="p">(</span><span class="n">value</span><span class="o">-></span><span class="n">data</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">v_pointer</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nb">NULL</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// Register the Base type</span>
<span class="n">GType</span>
<span class="nf">base_get_type</span><span class="w"> </span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="k">static</span><span class="w"> </span><span class="k">volatile</span><span class="w"> </span><span class="n">gsize</span><span class="w"> </span><span class="n">base_type__volatile</span><span class="p">;</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">g_once_init_enter</span><span class="w"> </span><span class="p">(</span><span class="o">&</span><span class="n">base_type__volatile</span><span class="p">))</span>
<span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="c1">// This is a derivable type; we also want to allow</span>
<span class="w"> </span><span class="c1">// its derived types to be derivable</span>
<span class="w"> </span><span class="k">static</span><span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="n">GTypeFundamentalInfo</span><span class="w"> </span><span class="n">finfo</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="p">(</span><span class="n">G_TYPE_FLAG_CLASSED</span><span class="w"> </span><span class="o">|</span>
<span class="w"> </span><span class="n">G_TYPE_FLAG_INSTANTIATABLE</span><span class="w"> </span><span class="o">|</span>
<span class="w"> </span><span class="n">G_TYPE_FLAG_DERIVABLE</span><span class="w"> </span><span class="o">|</span>
<span class="w"> </span><span class="n">G_TYPE_FLAG_DEEP_DERIVABLE</span><span class="p">),</span>
<span class="w"> </span><span class="p">};</span>
<span class="w"> </span><span class="c1">// The gunk for dealing with GValue</span>
<span class="w"> </span><span class="k">static</span><span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="n">GTypeValueTable</span><span class="w"> </span><span class="n">value_table</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">value_base_init</span><span class="p">,</span>
<span class="w"> </span><span class="n">value_base_free_value</span><span class="p">,</span>
<span class="w"> </span><span class="n">value_base_copy_value</span><span class="p">,</span>
<span class="w"> </span><span class="n">value_base_peek_pointer</span><span class="p">,</span>
<span class="w"> </span><span class="s">"p"</span><span class="p">,</span>
<span class="w"> </span><span class="n">value_base_collect_value</span><span class="p">,</span>
<span class="w"> </span><span class="s">"p"</span><span class="p">,</span>
<span class="w"> </span><span class="n">value_base_lcopy_value</span><span class="p">,</span>
<span class="w"> </span><span class="p">};</span>
<span class="w"> </span><span class="c1">// Base type information</span>
<span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="n">GTypeInfo</span><span class="w"> </span><span class="n">base_info</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="c1">// Class</span>
<span class="w"> </span><span class="k">sizeof</span><span class="w"> </span><span class="p">(</span><span class="n">GtkExpressionClass</span><span class="p">),</span>
<span class="w"> </span><span class="p">(</span><span class="n">GBaseInitFunc</span><span class="p">)</span><span class="w"> </span><span class="nb">NULL</span><span class="p">,</span>
<span class="w"> </span><span class="p">(</span><span class="n">GBaseFinalizeFunc</span><span class="p">)</span><span class="w"> </span><span class="nb">NULL</span><span class="p">,</span>
<span class="w"> </span><span class="p">(</span><span class="n">GClassInitFunc</span><span class="p">)</span><span class="w"> </span><span class="n">base_class_init</span><span class="p">,</span>
<span class="w"> </span><span class="p">(</span><span class="n">GClassFinalizeFunc</span><span class="p">)</span><span class="w"> </span><span class="nb">NULL</span><span class="p">,</span>
<span class="w"> </span><span class="nb">NULL</span><span class="p">,</span>
<span class="w"> </span><span class="c1">// Instance</span>
<span class="w"> </span><span class="k">sizeof</span><span class="w"> </span><span class="p">(</span><span class="n">GtkExpression</span><span class="p">),</span>
<span class="w"> </span><span class="mi">0</span><span class="p">,</span>
<span class="w"> </span><span class="p">(</span><span class="n">GInstanceInitFunc</span><span class="p">)</span><span class="w"> </span><span class="n">base_init</span><span class="p">,</span>
<span class="w"> </span><span class="c1">// GValue</span>
<span class="w"> </span><span class="o">&</span><span class="n">value_table</span><span class="p">,</span>
<span class="w"> </span><span class="p">};</span>
<span class="w"> </span><span class="c1">// Register the Base type as a new, abstract fundamental type</span>
<span class="w"> </span><span class="n">GType</span><span class="w"> </span><span class="n">base_type</span><span class="w"> </span><span class="o">=</span>
<span class="w"> </span><span class="n">g_type_register_fundamental</span><span class="w"> </span><span class="p">(</span><span class="n">g_type_fundamental_next</span><span class="w"> </span><span class="p">(),</span>
<span class="w"> </span><span class="n">g_intern_static_string</span><span class="w"> </span><span class="p">(</span><span class="s">"Base"</span><span class="p">),</span>
<span class="w"> </span><span class="o">&</span><span class="n">event_info</span><span class="p">,</span><span class="w"> </span><span class="o">&</span><span class="n">finfo</span><span class="p">,</span>
<span class="w"> </span><span class="n">G_TYPE_FLAG_ABSTRACT</span><span class="p">);</span>
<span class="w"> </span><span class="n">g_once_init_leave</span><span class="w"> </span><span class="p">(</span><span class="o">&</span><span class="n">base_type__volatile</span><span class="p">,</span><span class="w"> </span><span class="n">expression_type</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">base_type__volatile</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
</figure>
<p>Yes, this is <strong>a lot</strong> of code.</p>
<p>The base code stays pretty much the same:</p>
<figure class='code'>
<figcaption><span class="liquid-tags-code-filename">instance-type.c</span><span class="liquid-tags-code-lines">[Lines 191-243]</span><a href='/code/types/instance-type.c'>download</a></figcaption>
<div class="highlight"><pre><span></span><code><span class="k">static</span><span class="w"> </span><span class="kt">void</span>
<span class="nf">base_real_finalize</span><span class="w"> </span><span class="p">(</span><span class="n">Base</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="n">g_type_free_instance</span><span class="w"> </span><span class="p">((</span><span class="n">GTypeInstance</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="w"> </span><span class="n">self</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">static</span><span class="w"> </span><span class="kt">void</span>
<span class="nf">base_class_init</span><span class="w"> </span><span class="p">(</span><span class="n">BaseClass</span><span class="w"> </span><span class="o">*</span><span class="n">klass</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="n">klass</span><span class="o">-></span><span class="n">finalize</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">base_real_finalize</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">static</span><span class="w"> </span><span class="kt">void</span>
<span class="nf">base_init</span><span class="w"> </span><span class="p">(</span><span class="n">Base</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="c1">// Initialize the base instance fields</span>
<span class="w"> </span><span class="n">g_atomic_ref_count_init</span><span class="w"> </span><span class="p">(</span><span class="o">&</span><span class="n">res</span><span class="o">-></span><span class="n">ref_count</span><span class="p">);</span>
<span class="w"> </span><span class="n">res</span><span class="o">-></span><span class="n">some_field</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">42</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">static</span><span class="w"> </span><span class="n">Base</span><span class="w"> </span><span class="o">*</span>
<span class="nf">base_alloc</span><span class="w"> </span><span class="p">(</span><span class="n">GType</span><span class="w"> </span><span class="n">type</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="n">g_assert</span><span class="w"> </span><span class="p">(</span><span class="n">g_type_is_a</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="p">,</span><span class="w"> </span><span class="n">base_get_type</span><span class="p">());</span>
<span class="w"> </span><span class="c1">// Instantiate a new type derived by Base</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="p">(</span><span class="n">Base</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="w"> </span><span class="n">g_type_create_instance</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">Base</span><span class="w"> </span><span class="o">*</span>
<span class="nf">base_ref</span><span class="w"> </span><span class="p">(</span><span class="n">Base</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="n">g_atomic_ref_count_inc</span><span class="w"> </span><span class="p">(</span><span class="o">&</span><span class="n">self</span><span class="o">-></span><span class="n">ref_count</span><span class="p">);</span>
<span class="p">}</span>
<span class="kt">void</span>
<span class="nf">base_unref</span><span class="w"> </span><span class="p">(</span><span class="n">Base</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">g_atomic_ref_count_dec</span><span class="w"> </span><span class="p">(</span><span class="o">&</span><span class="n">self</span><span class="o">-></span><span class="n">ref_count</span><span class="p">))</span>
<span class="w"> </span><span class="n">BASE_GET_CLASS</span><span class="w"> </span><span class="p">(</span><span class="n">self</span><span class="p">)</span><span class="o">-></span><span class="n">finalize</span><span class="w"> </span><span class="p">(</span><span class="n">self</span><span class="p">);</span>
<span class="p">}</span>
<span class="kt">void</span>
<span class="nf">base_foo</span><span class="w"> </span><span class="p">(</span><span class="n">Base</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="n">BASE_GET_CLASS</span><span class="w"> </span><span class="p">(</span><span class="n">self</span><span class="p">)</span><span class="o">-></span><span class="n">foo</span><span class="w"> </span><span class="p">(</span><span class="n">self</span><span class="p">);</span>
<span class="p">}</span>
<span class="kt">void</span>
<span class="nf">base_bar</span><span class="w"> </span><span class="p">(</span><span class="n">Base</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="n">BASE_GET_CLASS</span><span class="w"> </span><span class="p">(</span><span class="n">self</span><span class="p">)</span><span class="o">-></span><span class="n">bar</span><span class="w"> </span><span class="p">(</span><span class="n">self</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div>
</figure>
<p>except:</p>
<ul>
<li>the reference counting is explicit, as we must use
<code>g_type_create_instance()</code> and <code>g_type_free_instance()</code> to allocate and
free the memory associated to the instance</li>
<li>you need to get the class structure from the instance using the GType
macros instead of direct pointer access</li>
</ul>
<p>Finally, you will need to add code to let you register derived types; since
we want to tightly control the derivation, we use an <em>ad hoc</em> structure for
the virtual functions, and we use a generic class initialization function:</p>
<figure class='code'>
<figcaption><span class="liquid-tags-code-filename">instance-type.c</span><span class="liquid-tags-code-lines">[Lines 245-290]</span><a href='/code/types/instance-type.c'>download</a></figcaption>
<div class="highlight"><pre><span></span><code><span class="k">static</span><span class="w"> </span><span class="kt">void</span>
<span class="nf">base_generic_class_init</span><span class="w"> </span><span class="p">(</span><span class="n">gpointer</span><span class="w"> </span><span class="n">g_class</span><span class="p">,</span>
<span class="w"> </span><span class="n">gpointer</span><span class="w"> </span><span class="n">class_data</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="n">BaseTypeInfo</span><span class="w"> </span><span class="o">*</span><span class="n">info</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">class_data</span><span class="p">;</span>
<span class="w"> </span><span class="n">BaseClass</span><span class="w"> </span><span class="o">*</span><span class="n">klass</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">g_class</span><span class="p">;</span>
<span class="w"> </span><span class="n">klass</span><span class="o">-></span><span class="n">finalize</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">info</span><span class="o">-></span><span class="n">finalize</span><span class="p">;</span>
<span class="w"> </span><span class="n">klass</span><span class="o">-></span><span class="n">foo</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">info</span><span class="o">-></span><span class="n">foo</span><span class="p">;</span>
<span class="w"> </span><span class="n">klass</span><span class="o">-></span><span class="n">bar</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">info</span><span class="o">-></span><span class="n">bar</span><span class="p">;</span>
<span class="w"> </span><span class="c1">// The info structure was copied, so we now need</span>
<span class="w"> </span><span class="c1">// to release the resources associated with it</span>
<span class="w"> </span><span class="n">g_free</span><span class="w"> </span><span class="p">(</span><span class="n">class_data</span><span class="p">);</span>
<span class="p">}</span>
<span class="c1">// Register a derived typed of Base</span>
<span class="k">static</span><span class="w"> </span><span class="n">GType</span>
<span class="nf">base_type_register_static</span><span class="w"> </span><span class="p">(</span><span class="k">const</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">type_name</span><span class="p">,</span>
<span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="n">BaseTypeInfo</span><span class="w"> </span><span class="o">*</span><span class="n">type_info</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="c1">// Simple check to ensure that the derived instance includes the</span>
<span class="w"> </span><span class="c1">// parent base type</span>
<span class="w"> </span><span class="n">g_assert</span><span class="w"> </span><span class="p">(</span><span class="n">type_info</span><span class="o">-></span><span class="n">instance_size</span><span class="w"> </span><span class="o">>=</span><span class="w"> </span><span class="k">sizeof</span><span class="w"> </span><span class="p">(</span><span class="n">Base</span><span class="p">));</span>
<span class="w"> </span><span class="n">GTypeInfo</span><span class="w"> </span><span class="n">type_info</span><span class="p">;</span>
<span class="w"> </span><span class="c1">// All derived types have the same class and cannot add new virtual</span>
<span class="w"> </span><span class="c1">// functions</span>
<span class="w"> </span><span class="n">type_info</span><span class="p">.</span><span class="n">class_size</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">sizeof</span><span class="w"> </span><span class="p">(</span><span class="n">BaseClass</span><span class="p">);</span>
<span class="w"> </span><span class="n">type_info</span><span class="p">.</span><span class="n">base_init</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">NULL</span><span class="p">;</span>
<span class="w"> </span><span class="n">type_info</span><span class="p">.</span><span class="n">base_finalize</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">NULL</span><span class="p">;</span>
<span class="w"> </span><span class="c1">// Fill out the class vtable from the BaseTypeInfo structure</span>
<span class="w"> </span><span class="n">type_info</span><span class="p">.</span><span class="n">class_init</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">base_generic_class_init</span><span class="p">;</span>
<span class="w"> </span><span class="n">type_info</span><span class="p">.</span><span class="n">class_finalize</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">NULL</span><span class="p">;</span>
<span class="w"> </span><span class="n">type_info</span><span class="p">.</span><span class="n">class_data</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">g_memdup</span><span class="w"> </span><span class="p">(</span><span class="n">type_info</span><span class="p">,</span><span class="w"> </span><span class="k">sizeof</span><span class="w"> </span><span class="p">(</span><span class="n">BaseTypeInfo</span><span class="p">));</span>
<span class="w"> </span><span class="c1">// Instance information</span>
<span class="w"> </span><span class="n">type_info</span><span class="p">.</span><span class="n">instance_size</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">type_info</span><span class="o">-></span><span class="n">instance_size</span><span class="p">;</span>
<span class="w"> </span><span class="n">type_info</span><span class="p">.</span><span class="n">n_preallocs</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span>
<span class="w"> </span><span class="n">type_info</span><span class="p">.</span><span class="n">instance_init</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">NULL</span><span class="p">;</span>
<span class="w"> </span><span class="n">type_info</span><span class="p">.</span><span class="n">value_table</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">NULL</span><span class="p">;</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">g_type_register_static</span><span class="w"> </span><span class="p">(</span><span class="n">BASE_TYPE</span><span class="p">,</span><span class="w"> </span><span class="n">type_name</span><span class="p">,</span><span class="w"> </span><span class="o">&</span><span class="n">type_info</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div>
</figure>
<p>Otherwise, you could re-use the <code>G_DEFINE_TYPE</code> macro—yes, it does not
require GObject—but then you’d have to implement your own class
initialization and instance initialization functions.</p>
<p>After you defined the base type, you can structure your types in the same
way as the boxed type code:</p>
<figure class='code'>
<figcaption><span class="liquid-tags-code-filename">instance-type.c</span><span class="liquid-tags-code-lines">[Lines 294-347]</span><a href='/code/types/instance-type.c'>download</a></figcaption>
<div class="highlight"><pre><span></span><code><span class="k">typedef</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">Base</span><span class="w"> </span><span class="n">parent</span><span class="p">;</span>
<span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">some_other_field</span><span class="p">;</span>
<span class="p">}</span><span class="w"> </span><span class="n">DerivedA</span><span class="p">;</span>
<span class="k">static</span><span class="w"> </span><span class="kt">void</span>
<span class="nf">derived_a_finalize</span><span class="w"> </span><span class="p">(</span><span class="n">Base</span><span class="w"> </span><span class="o">*</span><span class="n">base</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="n">DerivedA</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="n">DerivedA</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="w"> </span><span class="n">base</span><span class="p">;</span>
<span class="w"> </span><span class="n">g_free</span><span class="w"> </span><span class="p">(</span><span class="n">self</span><span class="o">-></span><span class="n">some_other_field</span><span class="p">);</span>
<span class="w"> </span><span class="c1">// We need to chain up to the parent's finalize() or we're</span>
<span class="w"> </span><span class="c1">// going to leak the instance</span>
<span class="w"> </span><span class="n">BASE_SUPER</span><span class="w"> </span><span class="p">(</span><span class="n">self</span><span class="p">)</span><span class="o">-></span><span class="n">finalize</span><span class="w"> </span><span class="p">(</span><span class="n">base</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">static</span><span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="n">BaseTypeInfo</span><span class="w"> </span><span class="n">derived_a_info</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="p">.</span><span class="n">instance_size</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">sizeof</span><span class="w"> </span><span class="p">(</span><span class="n">DerivedA</span><span class="p">),</span>
<span class="w"> </span><span class="p">.</span><span class="n">finalize</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">derived_a_finalize</span><span class="p">,</span>
<span class="w"> </span><span class="p">.</span><span class="n">foo</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">derived_a_foo</span><span class="p">,</span><span class="w"> </span><span class="c1">// defined elsewhere</span>
<span class="w"> </span><span class="p">.</span><span class="n">bar</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">derived_a_bar</span><span class="p">,</span><span class="w"> </span><span class="c1">// defined elsewhere</span>
<span class="p">};</span>
<span class="n">GType</span>
<span class="nf">derived_a_get_type</span><span class="w"> </span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="k">static</span><span class="w"> </span><span class="k">volatile</span><span class="w"> </span><span class="n">gsize</span><span class="w"> </span><span class="n">derived_type__volatile</span><span class="p">;</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">g_once_init_enter</span><span class="w"> </span><span class="p">(</span><span class="o">&</span><span class="n">derived_type__volatile</span><span class="p">))</span>
<span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="c1">// Register the type</span>
<span class="w"> </span><span class="n">GType</span><span class="w"> </span><span class="n">derived_type</span><span class="w"> </span><span class="o">=</span>
<span class="w"> </span><span class="n">base_type_register_static</span><span class="w"> </span><span class="p">(</span><span class="n">g_intern_static_string</span><span class="w"> </span><span class="p">(</span><span class="s">"DerivedA"</span><span class="p">),</span>
<span class="w"> </span><span class="o">&</span><span class="n">derived_a_info</span><span class="p">);</span>
<span class="w"> </span><span class="n">g_once_init_leave</span><span class="w"> </span><span class="p">(</span><span class="o">&</span><span class="n">derived_type__volatile</span><span class="p">,</span><span class="w"> </span><span class="n">derived_type</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">derived_type__volatile</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">Base</span><span class="w"> </span><span class="o">*</span>
<span class="nf">derived_a_new</span><span class="w"> </span><span class="p">(</span><span class="k">const</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">some_other_field</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="n">Base</span><span class="w"> </span><span class="o">*</span><span class="n">res</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">base_alloc</span><span class="w"> </span><span class="p">(</span><span class="n">derived_a_get_type</span><span class="w"> </span><span class="p">());</span>
<span class="w"> </span><span class="n">DerivedA</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="n">DerivedA</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="w"> </span><span class="n">res</span><span class="p">;</span>
<span class="w"> </span><span class="n">self</span><span class="o">-></span><span class="n">some_other_field</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">g_strdup</span><span class="w"> </span><span class="p">(</span><span class="n">some_other_field</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">res</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
</figure>
<p>The nice bit is that you can tell the introspection scanner how to deal with
each derived type through annotations, and keep the <span class="caps">API</span> simple to use in C
while idiomatic to use in other languages:</p>
<figure class='code'>
<figcaption><span class="liquid-tags-code-filename">instance-type.c</span><span class="liquid-tags-code-lines">[Lines 349-363]</span><a href='/code/types/instance-type.c'>download</a></figcaption>
<div class="highlight"><pre><span></span><code><span class="cm">/**</span>
<span class="cm"> * derived_a_get_some_other_field:</span>
<span class="cm"> * @base: (type DerivedA): a derived #Base instance</span>
<span class="cm"> *</span>
<span class="cm"> * Retrieves the `some_other_field` of a derived #Base instance.</span>
<span class="cm"> *</span>
<span class="cm"> * Returns: (transfer none): the contents of the field</span>
<span class="cm"> */</span>
<span class="k">const</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span>
<span class="nf">derived_a_get_some_other_field</span><span class="w"> </span><span class="p">(</span><span class="n">Base</span><span class="w"> </span><span class="o">*</span><span class="n">base</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="n">DerivedA</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="n">DerivedA</span><span class="w"> </span><span class="o">*</span><span class="p">)</span><span class="w"> </span><span class="n">base</span><span class="p">;</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">self</span><span class="o">-></span><span class="n">some_other_field</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
</figure>
<h3>Cost-benefit</h3>
<p>Of course, there are costs to this approach. In no particular order:</p>
<ul>
<li>
<p>The type system boilerplate is <em>a lot</em>; the code size more than doubled
from the boxed type approach. This is quite annoying, but at least it is
a one-off cost, and you won’t likely ever need to change it. It would be
nice to have it hidden by some magic macro incantation, but it’s
understandably hard to do so without imposing restrictions on the kind of
types you can create; since you’re trying to escape the restrictions of
GObject, it would not make sense to impose a different set of restrictions.</p>
</li>
<li>
<p>If you want to be able to use this new type with properties and you
cannot use <code>G_TYPE_POINTER</code> as a generic, hands-off container, you
<strong>will</strong> need to derive <code>GParamSpec</code>, and add <em>ad hoc</em> <span class="caps">API</span> for <code>GValue</code>,
which is even more annoying boilerplate. I’ve skipped it in the example,
because that would add about 100 more lines of code.</p>
</li>
<li>
<p>Generated signal marshallers, and the generic one using libffi, do not
know how to marshal typed instances; you will need custom written
marshallers, or you’re going to use <code>G_TYPE_POINTER</code> everywhere and
assume the risk of untyped boxing. The same applies to anything that uses
the type system to perform things like serialization and deserialization,
or <code>GValue</code> boxing and unboxing. You decided to build your own theme park
on the Moon, and the type system has no idea how to represent it, or
access its functionality.</p>
</li>
<li>
<p>Language bindings need to be able to deal with <code>GTypeInstance</code> and
fundamental types; this is not always immediately necessary, so some
maintainers do not add the code to handle this aspect of the type system.</p>
</li>
</ul>
<p>The benefit is, of course, the fact that you are using a separate type
hierarchy, and you get to make your own rules on things like memory
management, lifetimes, and ownership. You can control the inheritance chain,
and the rules on the overridable virtual functions. Since you control the
whole type, you can add things like serialization and deserialization, or
instance cloning, right at the top of the hierarchy. You could even
implement properties without using <code>GParamSpec</code>.</p>
<h3>Conclusion</h3>
<p>Please, please use GObject. Writing type system code is already boring and
error prone, which is why we added a ton of macros to avoid people shooting
themselves in both their feet, and we hammered away all the special
snowflake <span class="caps">API</span> flourishes that made parsing C <span class="caps">API</span> to generate introspection
data impossible.</p>
<p>I can only recommend you go down the <code>GTypeInstance</code> route if you’ve done
your due diligence on what that entails, and are aware that it is a last
resort if GObject simply does not work within your project’s constraints.</p>Another layer2019-08-15T10:16:00+01:002019-08-15T10:38:29+01:00ebassitag:www.bassi.io,2019-08-15:/articles/2019/08/15/another-layer/<p>In which I announce a new release of Graphene</p><p><a href="https://www.bassi.io/articles/2014/05/03/graphene/">Five years (and change) ago</a> I was looking
at the data types and <span class="caps">API</span> that were needed to write a 3D-capable scene graph
<span class="caps">API</span>; I was also learning about <span class="caps">SIMD</span> instructions and compiler builtins on <span class="caps">IA</span>
and <span class="caps">ARM</span>, as well as a bunch of math I didn’t really study in my brush offs
with formal higher education. The result was a small library called
<a href="https://ebassi.github.io/graphene">Graphene</a>.</p>
<p>Over the years I <a href="https://www.bassi.io/articles/2014/12/06/layer-by-layer/">added more <span class="caps">API</span></a>, <a href="https://www.bassi.io/articles/2016/06/09/experiments-in-meson/">moved the
build system from Autotools over to Meson</a>,
and <a href="https://www.bassi.io/articles/2019/03/14/a-little-testing/">wrote a whole separate library for its test suite</a>.</p>
<p>In the meantime, GStreamer started using Graphene in its <span class="caps">GL</span> element; <span class="caps">GTK</span>
3.9x is very much using Graphene internally and exposing it as public <span class="caps">API</span>;
Mutter developers are working on reimplementing the various math types in
their copies of Cogl and Clutter using Graphene; and
<a href="https://blogs.gnome.org/alexl">Alex</a> wrote <a href="https://github.com/alexlarsson/gthree">an entire 3D
engine</a> using it.</p>
<p>Not bad for a side project.</p>
<p>Of course, now I’ll have to start maintaining Graphene like a proper
grownup, which also means reporting its changes, bug fixes, and features
when I’m at the end of a development cycle.</p>
<p>While the 1.8 development cycle consisted mostly of bug fixes with no new
<span class="caps">API</span>, there have been a few major internal changes during the development
cycle towards 1.10:</p>
<ul>
<li>I rewrote the Euler angles conversion to and from quaternions and
matrices; the original implementation I cribbed from here and there was
not really adequate, and broke pretty horribly when you tried to
roundtrip from Euler angles to a transformation matrix and back. This
also affected the conversion between Euler angles and quaternions. The
new implementation is more correct, and as a side effect it now includes
not just the Tait–Bryan angles, but also the classic Euler angles. All
possible orders are available in both the intrinsic and extrinsic axes variants.</li>
<li>We’re dealing with floating point comparison and with infinities a bit
better, now; this is usually necessary because the various vector
implementations may have different behaviour, depending on the toolchain
in use. A shout out goes to Intel, who bothered to add an instruction to
check for infinities only in <span class="caps">AVX</span> 512, making it pointless for me, and
causing a lot more grief than necessary.</li>
<li>The <span class="caps">ARM</span> <span class="caps">NEON</span> implementation of <code>graphene_simd4f_t</code> has been fixed and
tested on actual <span class="caps">ARM</span> devices (an old Odroid I had lying around for ARMv7
and a Raspberry Pi3 for Aarch64); this means that the “this is experimental”
compiler warning has been removed. I still need to run the <span class="caps">CI</span> on an <span class="caps">ARM</span>
builder, but at least I can check if I’m doing something dumb, now.</li>
<li>As mentioned in the blog posts above, the whole test suite has been
rewritten using µTest, which dropped a dependency on GLib; you still need
GLib to get the integration with GObject, but if you’re not using that,
Graphene should now be easier to build and test.</li>
</ul>
<p>On the <span class="caps">API</span> side:</p>
<ul>
<li>there are a bunch of new functions for <code>graphene_rect_t</code>, courtesy of
Georges Basile Stavracas Neto and Marco Trevisan</li>
<li>thanks to Marco, the <code>graphene_rect_round()</code> function has been deprecated
in favour of the more reliable <code>graphene_rect_round_extents()</code></li>
<li><code>graphene_quaternion_t</code> gained new operators, like <code>add()</code>, <code>multiply()</code>
and <code>scale()</code></li>
<li>thanks to Alex Larsson, <code>graphene_plane_t</code> can now be transformed using
a matrix</li>
<li>I added equality and near-equality operators for <code>graphene_matrix_t</code>,
and a getter function to retrieve the translation components of a
transformation matrix</li>
<li>I added interpolation functions for the 2, 3, and 4-sized vectors</li>
<li>I’m working on exposing the matrix decomposition code for Gthree, but
that requires some untangling of messy code so I’ll be in the next
development snapshot.</li>
</ul>
<p>On the documentation side:</p>
<ul>
<li>I’ve reworked the contribution guide, and added a code of conduct to the
project; doesn’t matter how many times you say “patches welcome” if you
also aren’t clear on how those patches should be written, submitted,
and reviewed, and if you aren’t clear on what constitutes acceptable
behaviour when it comes to interactions between contributors and the maintainer</li>
<li>this landed at the tail end of 1.8, but I’ve hopefully clearly
documented the conventions of the matrix/matrix and matrix/vector
operations, to the point that people can use the Graphene <span class="caps">API</span> without
necessarily having to read the code to understand how to use it</li>
</ul>
<p>This concludes the changes that will appear with the next 1.10 stable
release, which will be available by the time <span class="caps">GNOME</span> 3.34 is out. For the
time being, you can check out <a href="https://github.com/ebassi/graphene/releases/tag/1.9.6">the latest development
snapshot</a> available
on Github.</p>
<p>I don’t have many plans for the future, to be quite honest; I’ll keep an
eye out for what <span class="caps">GTK</span> and Gthree need, and I expect that once Mutter starts
using Graphene I’ll start receiving bug reports.</p>
<p>One thing I did try was moving to a “static” <span class="caps">API</span> reference using
<a href="http://casual-effects.com/markdeep">Markdeep</a>, just like I did for µTest,
and drop yet another dependency; sadly, since we need to use gtk-doc
annotations for the GObject introspection data generation, we’re going to
depend on gtk-doc for a little while longer.</p>
<p>Of course, if you are using Graphene and you find some missing
functionality, feel free to <a href="https://github.com/ebassi/graphene/issues/new">open an issue</a>,
or a merge request.</p>More little testing2019-06-01T12:15:00+01:002023-02-20T00:32:07+00:00ebassitag:www.bassi.io,2019-06-01:/articles/2019/06/01/more-little-testing/<p>In which I announce that Graphene moved to a new testing <span class="caps">API</span></p><p>Back in March, <a href="https://www.bassi.io/articles/2019/03/14/a-little-testing/">I wrote about µTest</a>, a
<a href="https://en.wikipedia.org/wiki/Behavior-driven_development">Behavior-Driven Development</a> testing <span class="caps">API</span> for C libraries,
and that I was planning to use it to replace the GLib testing <span class="caps">API</span> in
<a href="https://ebassi.github.io/graphene/">Graphene</a>.</p>
<p>As I was busy with other things in <span class="caps">GTK</span>, it took me a while to get back
to µTest—especially because I needed some time to set up a development
environment on Windows in order to port µTest there. I managed to find
some time over various weekends and evenings, and ended up fixing a couple
of small issues here and there, to the point that I could run µTest’s own
test suite on my Windows 10 box, and then get the <span class="caps">CI</span> build job I have on
Appveyor to succeed as well.</p>
<p><figure>
<figcaption class="image-caption">
<p>Setting up <span class="caps">MSYS2</span> was the most time consuming bit, really</p>
</figcaption>
<div><img src="https://www.bassi.io/images/mutest-msys2.png"/></div>
</figure></p>
<p>While at it, I also cleaned up the <span class="caps">API</span> and properly documented it.</p>
<p>Since depending on gtk-doc would defeat the purpose, and since I honestly
dislike Doxygen, I was looking for a way to write the <span class="caps">API</span> reference and
publish it as <span class="caps">HTML</span>. As luck would have it, I remembered a mention on Twitter
about <a href="http://casual-effects.com/markdeep/">Markdeep</a>, a self-contained bit of JavaScript capable of
turning a Markdown document into a half decent <span class="caps">HTML</span> page client side.
Coupled with GitHub pages, I ended up with a fairly decent <a href="https://ebassi.github.io/mutest/mutest.md.html">online <span class="caps">API</span>
reference</a> that also works offline, falls back to a Markdown
document when not running through JavaScript, and can get fixed via pull requests.</p>
<p>Now that µTest is in a decent state, I ported the Graphene test suite over
to it and, now I can run it on Windows using <span class="caps">MSVC</span>—and <span class="caps">MSYS2</span>, as soon as the
issue with <span class="caps">GCC</span> gets fixed upstream. This means that, hopefully, we won’t
have regressions on Windows in the future.</p>
<p>The µTest <span class="caps">API</span> is small enough, now, that I don’t plan major changes; I don’t
want to commit to full <span class="caps">API</span> stability <strong>just yet</strong>, but I think we’re getting
close to a first stable release soon; definitely before Graphene 1.10 gets released.</p>
<p>In case you think this could be useful for you: feedback, in the form of
<a href="https://github.com/ebassi/mutest/issues">issues</a> and <a href="https://github.com/ebassi/mutest/pulls">pull requests</a>, is welcome.</p>(New) Adventures in CI2019-04-13T17:33:00+01:002019-04-14T17:02:00+01:00ebassitag:www.bassi.io,2019-04-13:/articles/2019/04/13/adventures-in-ci/<p>In which I talk about test reports with GitLab <span class="caps">CI</span></p><p>One of the great advantages of moving the code hosting in <a href="https://gitlab.gnome.org"><span class="caps">GNOME</span></a>
to <a href="https://gitlab.com">GitLab</a> is the ability to run per-project, per-branch, and
per-merge request continuous integration pipelines. While we’ve had a <span class="caps">CI</span>
pipeline for <a href="https://build.gnome.org">the whole of <span class="caps">GNOME</span></a> since 2012, it is limited to
the <code>master</code> branch of everything, so it only helps catching build issues
post-merge. Additionally, we haven’t been able to run test suites on
Continuous since early 2016.</p>
<p>Being able to run your test suite is, of course, great—assuming you do have
a test suite, and you’re good at keeping it working; gating all merge
requests on whether your <span class="caps">CI</span> pipeline passes or fails is incredibly powerful,
as it not only keeps your from unknowingly merging broken code, but it also
nudges you in the direction of never pushing commits to the <code>master</code> branch.
The downside is that it lacks nuance; if your test suite is composed of
hundreds of tests you need a way to know at a glance which ones failed.
Going through the job log is kind of crude, and it’s easy to miss things.</p>
<p>Luckily for us, GitLab has <a href="https://docs.gitlab.com/ee/ci/junit_test_reports.html">the ability to create a cover
report</a> for your test suite results, and present it on the
merge request summary, if you generate an <span class="caps">XML</span> report and tell the <span class="caps">CI</span>
machinery where you put it:</p>
<div class="highlight"><pre><span></span><code><span class="w"> </span><span class="nt">artifacts</span><span class="p">:</span>
<span class="w"> </span><span class="nt">reports</span><span class="p">:</span>
<span class="w"> </span><span class="nt">junit</span><span class="p">:</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="s">"${CI_PROJECT_DIR}/_build/report.xml"</span>
</code></pre></div>
<p>Sadly, the <span class="caps">XML</span> format chosen by GitLab is the one generated by
<a href="https://junit.org/junit5/">JUnit</a>, and we aren’t really writing Java classes. The JUnit <span class="caps">XML</span>
format is woefully underdocumented, with only <a href="https://llg.cubic.org/docs/junit/">an unofficial
breakdown</a> of the entities and structure available. On top of
that, since JUnit’s <span class="caps">XML</span> format is undocumented, GitLab has its <a href="https://gitlab.com/gitlab-org/gitlab-ce/issues/50964">own
quirks</a> in how it parses it.</p>
<p>Okay, assuming we have nailed down the output, how about the input? Since
we’re using <a href="http://mesonbuild.com/">Meson</a> on various projects, we can rely on <a href="http://mesonbuild.com/IDE-integration.html#tests">machine
parseable logs</a> for the test suite log. Unfortunately, Meson
currently outputs something that is not really valid <span class="caps">JSON</span>—you have to break
the log into separate lines, and parse each line into a <span class="caps">JSON</span> object, which
is somewhat less than optimal. Hopefully future versions of Meson will
generate an actual <span class="caps">JSON</span> file, and reduce the overhead in the tooling
consuming Meson files.</p>
<p>Nevertheless, after an afternoon of figuring out Meson’s output, and reverse
engineering the JUnit <span class="caps">XML</span> format and the GitLab JUnit parser, I managed to
write <a href="https://gist.github.com/ebassi/e5296ec77ae9e0d3a33fd483b5613b09">a simple script</a> that translates Meson’s
<code>testlog.json</code> file into a JUnit <span class="caps">XML</span> report that you can use with GitLab
after you ran the test suite in your <span class="caps">CI</span> pipeline. For instance, this is what
<span class="caps">GTK</span> does:</p>
<div class="highlight"><pre><span></span><code><span class="nb">set</span><span class="w"> </span>+e
xvfb-run<span class="w"> </span>-a<span class="w"> </span>-s<span class="w"> </span><span class="s2">"-screen 0 1024x768x24"</span><span class="w"> </span><span class="se">\</span>
<span class="w"> </span>meson<span class="w"> </span><span class="nb">test</span><span class="w"> </span><span class="se">\</span>
<span class="w"> </span>-C<span class="w"> </span>_build<span class="w"> </span><span class="se">\</span>
<span class="w"> </span>--timeout-multiplier<span class="w"> </span><span class="m">2</span><span class="w"> </span><span class="se">\</span>
<span class="w"> </span>--print-errorlogs<span class="w"> </span><span class="se">\</span>
<span class="w"> </span>--suite<span class="o">=</span>gtk<span class="w"> </span><span class="se">\</span>
<span class="w"> </span>--no-suite<span class="o">=</span>gtk:gsk<span class="w"> </span><span class="se">\</span>
<span class="w"> </span>--no-suite<span class="o">=</span>gtk:a11y
<span class="c1"># Save the exit code, so we can reuse it</span>
<span class="c1"># later to pass/fail the job</span>
<span class="nv">exit_code</span><span class="o">=</span><span class="nv">$?</span>
<span class="c1"># We always run the report generator, even</span>
<span class="c1"># if the tests failed</span>
<span class="nv">$srcdir</span>/.gitlab-ci/meson-junit-report.py<span class="w"> </span><span class="se">\</span>
<span class="w"> </span>--project-name<span class="o">=</span>gtk<span class="w"> </span><span class="se">\</span>
<span class="w"> </span>--job-id<span class="o">=</span><span class="s2">"</span><span class="si">${</span><span class="nv">CI_JOB_NAME</span><span class="si">}</span><span class="s2">"</span><span class="w"> </span><span class="se">\</span>
<span class="w"> </span>--output<span class="o">=</span>_build/<span class="si">${</span><span class="nv">CI_JOB_NAME</span><span class="si">}</span>-report.xml<span class="w"> </span><span class="se">\</span>
<span class="w"> </span>_build/meson-logs/testlog.json
<span class="nb">exit</span><span class="w"> </span><span class="nv">$exit_code</span>
</code></pre></div>
<p>Which results in this:</p>
<p><figure>
<figcaption class="image-caption">
<p>Some assembly required; those are <span class="caps">XFAIL</span> reftests, but JUnit doesn’t understand the concept</p>
</figcaption>
<div><img src="https://www.bassi.io/images/gitlab-cover-report.png"/></div>
</figure></p>
<p>The JUnit cover report in GitLab is <a href="https://gitlab.com/gitlab-org/gitlab-ce/issues/54067">only shown inside the merge request
summary</a>, so it’s not entirely useful if you’re developing in a branch
without opening an <span class="caps">MR</span> immediately after you push to the repository. I prefer
working on feature branches and getting the <span class="caps">CI</span> to run on my changes without
necessarily having to care about opening the <span class="caps">MR</span> until my work is ready for
review—especially since GitLab is not a speed demon when it comes to MRs
with lots of rebases/fixup commits in them. Having a summary of the test
suite results in that case is still useful, so I wrote <a href="https://gist.github.com/ebassi/0c6189bb9b414672ad36b9f984624843">a small conversion
script</a> that takes the <code>testlog.json</code> and turns it into
an <span class="caps">HTML</span> page, with a bit of <a href="http://jinja.pocoo.org/">Jinja</a> templating thrown into it to
avoid hardcoding the whole thing into string chunks. Like the JUnit
generator above, we can call the <span class="caps">HTML</span> generator right after running the test suite:</p>
<div class="highlight"><pre><span></span><code><span class="nv">$srcdir</span>/.gitlab-ci/meson-html-report.py<span class="w"> </span><span class="se">\</span>
<span class="w"> </span>--project-name<span class="o">=</span>GTK<span class="w"> </span><span class="se">\</span>
<span class="w"> </span>--job-id<span class="o">=</span><span class="s2">"</span><span class="si">${</span><span class="nv">CI_JOB_NAME</span><span class="si">}</span><span class="s2">"</span><span class="w"> </span><span class="se">\</span>
<span class="w"> </span>--output<span class="o">=</span>_build/<span class="si">${</span><span class="nv">CI_JOB_NAME</span><span class="si">}</span>-report.html<span class="w"> </span><span class="se">\</span>
<span class="w"> </span>_build/meson-logs/testlog.json
</code></pre></div>
<p>Then, we take the <span class="caps">HTML</span> file and store it as an artifact:</p>
<div class="highlight"><pre><span></span><code><span class="w"> </span><span class="nt">artifacts</span><span class="p">:</span>
<span class="w"> </span><span class="nt">when</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">always</span>
<span class="w"> </span><span class="nt">paths</span><span class="p">:</span>
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="s">"${CI_PROJECT_DIR}/_build/${CI_JOB_NAME}-report.html"</span>
</code></pre></div>
<p>And GitLab will store it for us, so that we can download it or view it in
the web <span class="caps">UI</span>.</p>
<p>There are additional improvements that can be made. For instance, the
reftests test suite in <span class="caps">GTK</span> generates images, and we’re already uploading
them as artifacts; since the image names are stable and determined by the
test name, we can create a link to them in the <span class="caps">HTML</span> report itself, so we can
show the result of the failed tests. With some more fancy <span class="caps">HTML</span>, <span class="caps">CSS</span>, and
JavaScript, we could have a nicer output, with collapsible sections hiding
the full console log. If we had a place to upload test results from multiple
pipelines, we could even graph the trends in the test suite on a particular
branch, and track our improvements.</p>
<p>All of this is, of course, not incredibly novel; nevertheless, the network
effect of having a build system in Meson that lends itself to integration
with additional tooling, and a code hosting infrastructure with native <span class="caps">CI</span>
capabilities in GitLab, allows us to achieve really cool results with
minimal glue code.</p>A little testing2019-03-14T15:01:00+00:002023-02-20T00:32:32+00:00ebassitag:www.bassi.io,2019-03-14:/articles/2019/03/14/a-little-testing/<p>In which I present a small testing framework for C code</p><p>Years ago I started writing <a href="https://ebassi.github.io/graphene/">Graphene</a> as a small library of 3D
transformation-related math types to be used by <span class="caps">GTK</span> (and possibly Clutter,
even if that didn’t pan out until Georges <a href="https://gitlab.gnome.org/GNOME/mutter/merge_requests/458">started working</a>
on the Clutter fork inside Mutter).</p>
<p>Graphene’s only requirement is a C99 compiler and a decent toolchain capable
of either taking <span class="caps">SSE</span> builtins or support vectorization on appropriately
aligned types. This means that, unless you decide to enable the GObject
types for each Graphene type, Graphene doesn’t really need GLib types or
<span class="caps">API</span>—except that’s a bit of a lie.</p>
<p>As I wanted to test what I was doing, Graphene has an optional build time
dependency on GLib for its test suite; the library itself may not use
anything from GLib, but if you want to build and run the test suite then you
need to have GLib installed.</p>
<p>This build time dependency makes testing Graphene on Windows a lot more
complicated than it ought to be. For instance, I need to install a ton of
packages when using the <span class="caps">MSYS2</span> toolchain on the <span class="caps">CI</span> instance on AppVeyor,
which takes roughly 6 minutes each for the 32bit and the 64bit builds; and I
can’t build the test suite at all when using <span class="caps">MSVC</span>, because then I’d have to
download and build GLib as well—and just to access the GTest <span class="caps">API</span>, <em>which I
don’t even like</em>.</p>
<hr>
<h3>What’s wrong with GTest</h3>
<p><a href="https://developer.gnome.org/glib/stable/glib-Testing.html">GTest</a> is kind of problematic—outside of Google hijacking the
name of the <span class="caps">API</span> for their own testing framework, which makes looking for it
a pain. GTest is <strong>a lot</strong> more complicated than a small unit testing <span class="caps">API</span>
needs to be, for starters; it was originally written to be used with a
specific harness, <a href="https://developer.gnome.org/glib/stable/gtester.html">gtester</a>, in order to generate a very brief
<span class="caps">HTML</span> report using <a href="https://developer.gnome.org/glib/stable/gtester-report.html">gtester-report</a>, including some
timing information on each unit—except that <code>gtester</code> is now deprecated
because the build system gunk to make it work was terrible to deal with. So,
we pretty much told everyone to stop bothering, add a <code>--tap</code> argument when
calling every test binary, and use the <a href="https://testanything.org/"><span class="caps">TAP</span></a> harness in Autotools.</p>
<p>Of course, this means that the testing framework now has a completely
useless output format, and with it, a bunch of default behaviours driven by
said useless output format, and we’re still deciding if we should break
backward compatibility to ensure that the supported output format has a sane
default behaviour.</p>
<p>On top of that, GTest piggybacks on GLib’s own assertion mechanism, which
has two major downsides:</p>
<ul>
<li>it can be disabled at compile time by defining <code>G_DISABLE_ASSERT</code> before
including <code>glib.h</code>, which, surprise, people tend to use when releasing;
thus, you can’t run tests on builds that would most benefit from a test suite</li>
<li>it literally <code>abort()</code>s the test unit, which breaks any test harness
in existence that does not expect things to <code>SIGABRT</code> midway through
a test suite—which includes GLib’s own deprecated <code>gtester</code> harness</li>
</ul>
<p>To solve the first problem we added a lot of wrappers around <code>g_assert()</code>,
like <code>g_assert_true()</code> and <code>g_assert_no_error()</code>, that won’t be disabled
depending on your build options and thus won’t break your test suite—and if
your test suite is still using <code>g_assert()</code>, you’re strongly encouraged to
port to the newer <span class="caps">API</span>. The second issue is still standing, and makes running
GTest-based test suite under <em>any</em> harness a pain, but especially under a
<span class="caps">TAP</span> harness, which requires listing the amount of tests you’ve run, or that
you’re planning to run.</p>
<p>The remaining issues of GTest are the convoluted way to add tests using a
unique path; the bizarre pattern matching <span class="caps">API</span> for warnings and errors; the
whole sub-process <span class="caps">API</span> that relaunches the test binary and calls a single
test unit in order to allow it to assert safely and capture its output. It’s
very much <strong>the</strong> GLib test suite, except when it tries to use non-GLib <span class="caps">API</span>
internally, like the command line option parser, or its own logging
primitives; it’s also sorely lacking in the GObject/<span class="caps">GIO</span> side of things, so
you can’t use standard <span class="caps">API</span> to create a mock GObject type, or a mock GFile.</p>
<p>If you want to contribute to GLib, then working on improving the GTest <span class="caps">API</span>
would be a good investment of your time; since my project does not depend on
GLib, though, I had the chance of starting with a clean slate.</p>
<hr>
<h3>A clean slate</h3>
<p>For the last couple of years I’ve been playing off and on with a small test
framework <span class="caps">API</span>, mostly inspired by <a href="https://en.wikipedia.org/wiki/Behavior-driven_development"><span class="caps">BDD</span></a> frameworks like
<a href="https://mochajs.org/">Mocha</a> and <a href="https://jasmine.github.io/">Jasmine</a>. <em>Behaviour Driven Development</em> is
kind of a buzzword, like <em>test driven development</em>, but I particularly like
the idea of describing a test suite in terms of <em>specifications</em> and
<em>expectations</em>: you specify what a piece of code does, and you match results
to your expectations.</p>
<p>The <span class="caps">API</span> for describing the test suites is modelled on natural language
(assuming your language is English, sadly):</p>
<div class="highlight"><pre><span></span><code><span class="w"> </span><span class="nx">describe</span><span class="p">(</span><span class="s2">"your data type"</span><span class="p">,</span><span class="w"> </span><span class="kd">function</span><span class="p">()</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">it</span><span class="p">(</span><span class="s2">"does something"</span><span class="p">,</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="nx">expect</span><span class="p">(</span><span class="nx">doSomething</span><span class="p">()).</span><span class="nx">toBe</span><span class="p">(</span><span class="kc">true</span><span class="p">);</span>
<span class="w"> </span><span class="p">});</span>
<span class="w"> </span><span class="nx">it</span><span class="p">(</span><span class="s2">"can greet you"</span><span class="p">,</span><span class="w"> </span><span class="p">()</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="nx">greeting</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">getHelloWorld</span><span class="p">();</span>
<span class="w"> </span><span class="nx">expect</span><span class="p">(</span><span class="nx">greeting</span><span class="p">).</span><span class="nx">not</span><span class="p">.</span><span class="nx">toBe</span><span class="p">(</span><span class="s2">"Goodbye World"</span><span class="p">);</span>
<span class="w"> </span><span class="p">});</span>
<span class="w"> </span><span class="p">});</span>
</code></pre></div>
<p>Of course, C is more verbose that JavaScript, but we can adopt a similar mechanism:</p>
<div class="highlight"><pre><span></span><code><span class="k">static</span><span class="w"> </span><span class="kt">void</span>
<span class="nf">something</span><span class="w"> </span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="n">expect</span><span class="w"> </span><span class="p">(</span><span class="s">"doSomething"</span><span class="p">,</span>
<span class="w"> </span><span class="n">bool_value</span><span class="w"> </span><span class="p">(</span><span class="n">do_something</span><span class="w"> </span><span class="p">()),</span>
<span class="w"> </span><span class="n">to_be</span><span class="p">,</span><span class="w"> </span><span class="nb">true</span><span class="p">,</span>
<span class="w"> </span><span class="nb">NULL</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">static</span><span class="w"> </span><span class="kt">void</span>
<span class="p">{</span>
<span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">greeting</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">get_hello_world</span><span class="w"> </span><span class="p">();</span>
<span class="w"> </span><span class="n">expect</span><span class="w"> </span><span class="p">(</span><span class="s">"getHelloWorld"</span><span class="p">,</span>
<span class="w"> </span><span class="n">string_value</span><span class="w"> </span><span class="p">(</span><span class="n">greeting</span><span class="p">),</span>
<span class="w"> </span><span class="k">not</span><span class="p">,</span><span class="w"> </span><span class="n">to_be</span><span class="p">,</span><span class="w"> </span><span class="s">"Goodbye World"</span><span class="p">,</span>
<span class="w"> </span><span class="nb">NULL</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">static</span><span class="w"> </span><span class="kt">void</span>
<span class="n">type_suite</span><span class="w"> </span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="p">(</span><span class="s">"does something"</span><span class="p">,</span><span class="w"> </span><span class="n">do_something</span><span class="p">);</span>
<span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="p">(</span><span class="s">"can greet you"</span><span class="p">,</span><span class="w"> </span><span class="n">greet</span><span class="p">);</span>
<span class="p">}</span>
<span class="err">…</span>
<span class="w"> </span><span class="n">describe</span><span class="w"> </span><span class="p">(</span><span class="s">"your data type"</span><span class="p">,</span><span class="w"> </span><span class="n">type_suite</span><span class="p">);</span>
<span class="err">…</span>
</code></pre></div>
<p>If only C11 got blocks from Clang, this would look a lot less clunkier.</p>
<p>The value wrappers are also necessary, because C is only type safe as long
as every type you have is an integer.</p>
<p>Since we’re good C citizens, we should namespace the <span class="caps">API</span>, which requires
naming this library—let’s call it <a href="https://github.com/ebassi/mutest">µTest</a>, in a fit of unoriginality.</p>
<p>One of the nice bits of Mocha and Jasmine is the output of running a test suite:</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>./tests/general<span class="w"> </span>
<span class="w"> </span>General
<span class="w"> </span>contains<span class="w"> </span>at<span class="w"> </span>least<span class="w"> </span>a<span class="w"> </span>spec<span class="w"> </span>with<span class="w"> </span>an<span class="w"> </span>expectation
<span class="w"> </span>✓<span class="w"> </span>a<span class="w"> </span>is<span class="w"> </span><span class="nb">true</span>
<span class="w"> </span>✓<span class="w"> </span>a<span class="w"> </span>is<span class="w"> </span>not<span class="w"> </span><span class="nb">false</span>
<span class="w"> </span><span class="m">2</span><span class="w"> </span>passing<span class="w"> </span><span class="o">(</span><span class="m">219</span>.00<span class="w"> </span>µs<span class="o">)</span>
<span class="w"> </span>can<span class="w"> </span>contain<span class="w"> </span>multiple<span class="w"> </span>specs
<span class="w"> </span>✓<span class="w"> </span>str<span class="w"> </span>contains<span class="w"> </span><span class="s1">'hello'</span>
<span class="w"> </span>✓<span class="w"> </span>str<span class="w"> </span>contains<span class="w"> </span><span class="s1">'world'</span>
<span class="w"> </span>✓<span class="w"> </span>contains<span class="w"> </span>all<span class="w"> </span>fragments
<span class="w"> </span><span class="m">3</span><span class="w"> </span>passing<span class="w"> </span><span class="o">(</span><span class="m">145</span>.00<span class="w"> </span>µs<span class="o">)</span>
<span class="w"> </span>should<span class="w"> </span>be<span class="w"> </span>skipped
<span class="w"> </span>-<span class="w"> </span>skip<span class="w"> </span>this<span class="w"> </span><span class="nb">test</span>
<span class="w"> </span><span class="m">0</span><span class="w"> </span>passing<span class="w"> </span><span class="o">(</span><span class="m">31</span>.00<span class="w"> </span>µs<span class="o">)</span>
<span class="w"> </span><span class="m">1</span><span class="w"> </span>skipped
Total
<span class="m">5</span><span class="w"> </span>passing<span class="w"> </span><span class="o">(</span><span class="m">810</span>.00<span class="w"> </span>µs<span class="o">)</span>
<span class="m">1</span><span class="w"> </span>skipped
</code></pre></div>
<p>Or, with colors:</p>
<p><figure>
<figcaption class="image-caption">
<p>Using colors means immediately taking this more seriously</p>
</figcaption>
<div><img src="https://www.bassi.io/images/mutest-output-mocha.png"/></div>
</figure></p>
<p>The colours go automatically away if you redirect the output to something
that is not a <span class="caps">TTY</span>, so your logs won’t be messed up by escape sequences.</p>
<p>If you have a test harness, then you can use the <code>MUTEST_OUTPUT</code> environment
variable to control the output; for instance, if you’re using <span class="caps">TAP</span> you’ll get:</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span><span class="nv">MUTEST_OUTPUT</span><span class="o">=</span>tap<span class="w"> </span>./tests/general
<span class="c1"># General</span>
<span class="c1"># contains at least a spec with an expectation</span>
ok<span class="w"> </span><span class="m">1</span><span class="w"> </span>a<span class="w"> </span>is<span class="w"> </span><span class="nb">true</span>
ok<span class="w"> </span><span class="m">2</span><span class="w"> </span>a<span class="w"> </span>is<span class="w"> </span>not<span class="w"> </span><span class="nb">false</span>
<span class="c1"># can contain multiple specs</span>
ok<span class="w"> </span><span class="m">3</span><span class="w"> </span>str<span class="w"> </span>contains<span class="w"> </span><span class="s1">'hello'</span>
ok<span class="w"> </span><span class="m">4</span><span class="w"> </span>str<span class="w"> </span>contains<span class="w"> </span><span class="s1">'world'</span>
ok<span class="w"> </span><span class="m">5</span><span class="w"> </span>contains<span class="w"> </span>all<span class="w"> </span>fragments
<span class="c1"># should be skipped</span>
ok<span class="w"> </span><span class="m">6</span><span class="w"> </span><span class="c1"># skip: skip this test</span>
<span class="m">1</span>..6
</code></pre></div>
<p>Which can be passed through to <code>prove</code> to get:</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span><span class="nv">MUTEST_OUTPUT</span><span class="o">=</span>tap<span class="w"> </span>prove<span class="w"> </span>./tests/general
./tests/general<span class="w"> </span>..<span class="w"> </span>ok
All<span class="w"> </span>tests<span class="w"> </span>successful.
<span class="nv">Files</span><span class="o">=</span><span class="m">1</span>,<span class="w"> </span><span class="nv">Tests</span><span class="o">=</span><span class="m">6</span>,<span class="w"> </span><span class="m">0</span><span class="w"> </span>wallclock<span class="w"> </span>secs<span class="w"> </span><span class="o">(</span><span class="w"> </span><span class="m">0</span>.02<span class="w"> </span>usr<span class="w"> </span>+<span class="w"> </span><span class="m">0</span>.00<span class="w"> </span><span class="nv">sys</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0</span>.02<span class="w"> </span>CPU<span class="o">)</span>
Result:<span class="w"> </span>PASS
</code></pre></div>
<p>I’m planning to add some additional output formatters, like <span class="caps">JSON</span> and <span class="caps">XML</span>.</p>
<hr>
<h3>Using µTest</h3>
<p>Ideally, µTest should be used as a sub-module or a Meson sub-project of your
own; if you’re using it as a sub-project, you can tell Meson to build a
static library that won’t get installed on your system, e.g.:</p>
<div class="highlight"><pre><span></span><code><span class="n">mutest_dep</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">dependency</span><span class="p">(</span><span class="s">'mutest-1'</span><span class="p">,</span>
<span class="w"> </span><span class="n">fallback</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="s">'mutest'</span><span class="p">,</span><span class="w"> </span><span class="s">'mutest_dep'</span><span class="w"> </span><span class="p">],</span>
<span class="w"> </span><span class="n">default_options</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s">'static=true'</span><span class="p">],</span>
<span class="w"> </span><span class="n">required</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span>
<span class="w"> </span><span class="nb">disabler</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span>
<span class="p">)</span>
<span class="c"># Or, if you're using Meson < 0.49.0</span>
<span class="n">mutest_dep</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">dependency</span><span class="p">(</span><span class="s">'mutest-1'</span><span class="p">,</span><span class="w"> </span><span class="n">required</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">)</span>
<span class="k">if</span><span class="w"> </span><span class="ow">not</span><span class="w"> </span><span class="n">mutest_dep</span><span class="p">.</span><span class="n">found</span><span class="p">()</span>
<span class="w"> </span><span class="n">mutest</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">subproject</span><span class="p">(</span><span class="s">'mutest'</span><span class="p">,</span>
<span class="w"> </span><span class="n">default_options</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="s">'static=true'</span><span class="p">,</span><span class="w"> </span><span class="p">],</span>
<span class="w"> </span><span class="n">required</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span>
<span class="w"> </span><span class="p">)</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">mutest</span><span class="p">.</span><span class="n">found</span><span class="p">()</span>
<span class="w"> </span><span class="n">mutest_dep</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">mutest</span><span class="p">.</span><span class="n">get_variable</span><span class="p">(</span><span class="s">'mutest_dep'</span><span class="p">)</span>
<span class="w"> </span><span class="k">else</span>
<span class="w"> </span><span class="n">mutest_dep</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">disabler</span><span class="p">()</span>
<span class="w"> </span><span class="k">endif</span>
<span class="k">endif</span>
</code></pre></div>
<p>Then you can make the tests conditional on <code>mutest_dep.found()</code>.</p>
<p>µTest is kind of experimental, and I’m still breaking its <span class="caps">API</span> in places, as
a result of documenting it and trying it out, by <a href="https://github.com/ebassi/graphene/tree/mutest">porting the Graphene test
suite to it</a>. There’s still
a bunch of <span class="caps">API</span> that I’d like to land, like custom matchers/formatters for
complex data types, and a decent want to skip a specification or a whole
suite; plus, as I said above, some additional formatted output.</p>
<p>If you have feedback, feel free to open an issue—or a pull request <em>wink
wink nudge nudge</em>.</p>Episode 2.a: Building GNOME2019-02-24T17:00:00+00:002019-02-24T19:18:40+00:00ebassitag:www.bassi.io,2019-02-24:/articles/2019/02/24/history-of-gnome-episode-2-a/<p class="first last">A side episode! Building <span class="caps">GNOME</span> is complicated; releasing <span class="caps">GNOME</span> is even worse. We’re going to see what tools <span class="caps">GNOME</span> developers used to build <span class="caps">GNOME</span> 2.</p>
<p>In the <span class="caps">GNOME</span> project community, the people building the code are represented by
two separate but equally important groups: the maintainers, who release the
code, and the release team, who release <span class="caps">GNOME</span>. These are their stories.</p>
<hr class="docutils" />
<p>Developing a software project can be hard, but building one ought to be simpler,
right? After all, it’s kind of a binary state for software projects after any
change: either the change broke the build, or it didn’t—and broken builds do not
get released, right?</p>
<p>Oh, you sweet summer child. Of course it’s not that simple.</p>
<p>Building software gets even more complicated when it comes to complex,
interdependent projects composed by multiple modules like <span class="caps">GNOME</span>; each module has
dependencies lower in the stack, and reverse dependencies—that is, modules that
depend on the interfaces it provides—higher in the stack.</p>
<p>If you’re working on a component low in the stack, especially in 2002, chances
are you only have system dependencies, and those system dependencies are
generally shipped by your Linux distribution. Those dependencies do not
typically change very often, or at the very least they assume you’re okay with
not targeting the latest, bleeding edge version. If push comes to shove, you can
always write a bunch of fallback code that gets only ever tested by people
running on old operating systems—if they decide on a whim to try and compile the
latest and greatest application, instead of just waiting until their whole
platform gets an upgrade.</p>
<p>Moving upwards in the <span class="caps">GNOME</span> stack, you start having multiple dependencies, but
generally speaking those dependencies move at the same speed as your project, so
you can keep track of them. Unlike other projects, the <span class="caps">GNOME</span> platform never had
issues with the multiplication of dependencies—something that will come back to
bite the maintainers later in the 2.x development cycle—but even so, with <span class="caps">GNOME</span>
offering code hosting to like-minded developers, it didn’t use to be hard to
keep track of things; if everything happens on the same source code repository
infrastructure you can subscribe to the changes for your dependencies, and see
what happens almost in real time.</p>
<p>Applications, finally, are another beast entirely; here, dependencies can be
many, and spanning across different system services, different build systems,
different code hosting services, and different languages. To minimise the causes
of headaches, you can decide to target the versions packages by the Linux
distribution you’re using, unless you’re in the middle of a major <span class="caps">API</span> version
shift in the platform—like, say, the one that happened between <span class="caps">GNOME</span> 1 and 2.
No Linux distribution packager in their right mind would ship releases for
highly unstable core platform libraries, especially when the development cadence
of those libraries outpaces the cadence of the distribution packaging and update
process. For those cases, using snapshots of the source under revision control
is the only option left for you as an upstream maintainer.</p>
<p>So, at this point, your options are limited. You want to install the latest and
greatest version of your dependencies—unstable as they might be—on your local
system, but you don’t want to mess up the rest of the system in case the changes
introduce a bug. In other words: if you’re running <span class="caps">GNOME</span> as your desktop, and
you upgrade a dependency for your application, you might end up breaking the
your own desktop, and then spend time undoing the unholy mess you made, instead
of hacking on your own project.</p>
<p>This is usually the point where programmers start writing scripts to modify the
environment, and build dependencies and applications into their own separate
prefix, using small utilities that describe where libraries put their header
files and shared objects in order to construct the necessary compiler and linker
arguments. <span class="caps">GNOME</span> libraries standardised to one of these tools, called
pkg-config, before the 2.0 release, replacing the per-project tools like
glib-config or gtk-config that were common during the <span class="caps">GNOME</span> 1.x era.</p>
<p>Little by little, the scripts each developer used started to get shared, and
improved; for instance, instead of hard coding the list of things to build, and
the order in which they should be built, you may want to be able to describe a
whole project in terms of a list of modules to be built—each module pointing to
its source code repository, its configuration and build time options, and its
dependencies. Another useful feature is the ability to set up an environment
capable of building and running applications against the components you just built.</p>
<p>The most successful script for building and running <span class="caps">GNOME</span> components, <em>jhbuild</em>,
emerged in 2002. Written by James Henstridge, the maintainer of the <span class="caps">GTK</span> bindings
for Python, jhbuild took an <span class="caps">XML</span> description of a set of modules, built the
dependency tree for each component, and went through the set in the right order,
building everything it could, assuming it knew how to handle the module’s build
system—which, at the time, was mostly a choice between Autotools and Autotools.
Additionally, it could spawn a shell and let you run what you built, or compile
additional code as if you installed every component in a system location. If you
wanted to experience <span class="caps">GNOME</span> at its most bleeding edge, you could build a whole
module set into a system prefix like <cite>/opt</cite>, and point your session manager to
that location. Running a whole desktop environment out of a <span class="caps">CVS</span> snapshot: what
could possibly go wrong, right? Well, at least you had the option of going back
to the safe harbours of your Linux distibution’s packaged version of <span class="caps">GNOME</span>.</p>
<p>Little by little, over the years, jhbuild acquire new features, like the ability
to build projects that were not using Autotools. Multiple module sets, one for
each branch of <span class="caps">GNOME</span>, and one for tracking the latest and greatest, appeared
over the years, as well as additional module sets for building applications,
both hosted on gnome.org repositories and outside the <span class="caps">GNOME</span> infrastructure.
Jhbuild was even used on non-Linux platforms, like macOS, to build the core
<span class="caps">GNOME</span> platform stack, and let application developers port their work there.
Additionally, other projects like X11 and the freedesktop.org stack that we’ll
see in a future episode, will publish their own module sets, as many of the
developers in <span class="caps">GNOME</span> moved through the stack and brought their tools with them.</p>
<p>With jhbuild consuming sets of modules in order to build the <span class="caps">GNOME</span> stack, the
question becomes: who maintained those sets? Ideally, the release team would be
responsible for keeping the modules up to date whenever a new dependency was
added, or an old dependency removed. As the release team was responsible of
deciding which modules belonged in the <span class="caps">GNOME</span> release, they would be the ones
best positioned to update the jhbuild sets. There was a small snag in this plan,
though: the release team already had its own tool for building <span class="caps">GNOME</span> from
release archives produced and published by the module maintainers, in the
correct order, to verify that the whole <span class="caps">GNOME</span> release would build and produce
something that could be packaged by Linux (and non-Linux) distributors.</p>
<p>The release team’s tool was called <em><span class="caps">GARNOME</span></em>, and was based on the <span class="caps">GAR</span>
architecture designed by Nick Moffitt, which itself was largely based on the <span class="caps">BSD</span>
port system. <span class="caps">GARNOME</span> was developed as a way for the release team to build and
test alpha releases of <span class="caps">GNOME</span> during the 2.0 development cycle. The main
difference between jhbuild and <span class="caps">GARNOME</span> was the latter’s focus on release
archives, compared to the former’s focus on source checkouts. The main goal of
<span class="caps">GARNOME</span> was really to replicate the process of distributors taking various
releases and packaging them up in their preferred format. While editing
jhbuild’s module sets was a simple matter of changing some <span class="caps">XML</span>, the <span class="caps">GARNOME</span>
recipes were a fairly complicated set of Make files, with magic variables and
magic include directives that would lead to building and installing each module,
using Make rules to determine the dependencies. All of this meant that there was
not only no overlap between who used jhbuild and who used <span class="caps">GARNOME</span>, but also no
overlap between who contributed to which project.</p>
<p>Both jhbuild and <span class="caps">GARNOME</span> assumed you had a working system and development
toolchain for all the programming languages needed to build <span class="caps">GNOME</span> components;
they also relied on a whole host of system dependencies, especially when it came
to talking to system services and hardware devices. While this was relatively
less important for <span class="caps">GARNOME</span>, whose role was simply to build the whole of <span class="caps">GNOME</span>,
jhbuild started to suffer from these limitations as soon as <span class="caps">GNOME</span> projects began
interacting much more with the underlying services offered by the operating system.</p>
<p>It’s important to note that none of this stuff was automated; it all relied on
human intervention for testing that things would not blow up in interesting
ways. We were far, far away from any concept of a continuous integration
pipeline. Individual developers had to hunt down breakage in library releases
that would have repercussions down the line when building other libraries,
system components, or applications. The net result was that building <span class="caps">GNOME</span> was
only possible if everything was built out of release archives; anything else was
deeply unstable, and proved to be hard to handle for both seasoned developers
and new contributors alike, the more complexity was piled on the project.</p>
<p><span class="caps">GARNOME</span> was pretty successful, and ended up being used for a majority of the
<span class="caps">GNOME</span> 2 releases, until it was finally retired in favour of jhbuild itself,
using a special module set that pointed to release archives instead of source
code repositories. The module set was maintained by the release team, and
published for every <span class="caps">GNOME</span> release, to let other developers and packagers
reproduce and validate the process.</p>
<p>Jhbuild is still used to this day, mostly for the development of system
components like <span class="caps">GTK</span>, or the <span class="caps">GNOME</span> Shell; application building has largely
shifted towards containerised systems, like Flatpak, which have the advantage of
being easily automated in a <span class="caps">CI</span> environment. These systems are also much easier
to use from a newcomer perspective, and are quite more reliable when it comes to
the stability of the underlying middleware.</p>
<p>The release team switched away from jhbuild for validating and publishing <span class="caps">GNOME</span>
releases in 2018, long into the <span class="caps">GNOME</span> 3 release cycle, using a new tool called
BuildStream which not only builds the <span class="caps">GNOME</span> components but it also builds the
lower layers of the stack including the compiler toolchain, to ensure a level of
build reproducibility that jhbuild and <span class="caps">GARNOME</span> never had.</p>
<div class="section" id="references">
<h2>References</h2>
<ul class="simple">
<li><a class="reference external" href="https://www.bassi.io/category/history/podcast.xml">Subscribe</a> to this podcast</li>
<li>Music: “<a class="reference external" href="http://freemusicarchive.org/music/Lee_Rosevere/Music_For_Podcasts/Lee_Rosevere_-_Music_For_Podcasts_-_04_Looking_Back">Looking Back</a>“, by Lee Rosevere - <span class="caps">CC</span> <a class="reference external" href="https://creativecommons.org/licenses/by/4.0/">by-4.0</a></li>
<li>Logo: The History of <span class="caps">GNOME</span>, by <a class="reference external" href="http://jimmac.musichall.cz/">Jakub Steiner</a></li>
<li>This podcast is released under a <a class="reference external" href="https://creativecommons.org/licenses/by-sa/4.0/">Creative Commons license</a></li>
<li><a class="reference external" href="https://www.bassi.io/articles/2018/10/25/table-of-contents/">Table of contents</a>.</li>
<li><a class="reference external" href="https://gitlab.gnome.org/GNOME/jhbuild/">jhbuild project page</a>.</li>
<li><a class="reference external" href="http://web.archive.org/web/20020208034707/http://www.lnx-bbc.org/garchitecture.html"><span class="caps">GAR</span> Architecture [archive]</a>.</li>
<li><a class="reference external" href="https://mail.gnome.org/archives/gnome-announce-list/2002-January/msg00063.html"><span class="caps">GARNOME</span> Preview One</a>.</li>
</ul>
</div>
Episode 2.2: Release Day2019-02-14T16:00:00+00:002019-02-24T19:18:32+00:00ebassitag:www.bassi.io,2019-02-14:/articles/2019/02/14/history-of-gnome-episode-2-2/<p class="first last">The <span class="caps">GNOME</span> 2 release process meant re-evaluating everything that makes a desktop environment: from its design, to the design of all of its applications, to the release process, to the interaction of settings and preferences.</p>
<p>For all intents and purposes, the 2.0 release process of <span class="caps">GNOME</span> was a reboot of
the project; as such, it was a highly introspective event that just so happened
to result in a public release of a software platform used by a large number of
people. The real result of this process was not in the bits and bytes that were
compiled, or interpreted, into desktop components, panel applets, or
applications; it was, instead, a set of design tenets, a project ethos, and a
powerful marketing brand that exist to this day.</p>
<p>The <span class="caps">GNOME</span> community of developers, documenters, translators, and designers, was
faced with the result of Sun’s user testing, and with the feedback on
documentation, accessibility, and design, and had two choices: double down on
what everyone was doing, and maintain the existing contributor and user bases;
or adapt, take a gamble, and change—possibly even lose contributors, in the hope
of gaining more users, and more contributors, down the road.</p>
<p>The community opted for the latter gamble.</p>
<p>The decision was not without internal strife.</p>
<p>This kind of decisions is fairly common in all projects that reached a certain
critical mass, especially if they don’t have a central figure keeping everything
together, and being the ultimate arbiter of taste and direction. Miguel de Icaza
was already really busy with Ximian, and even if the Foundation created the
release team in order to get a “steering committee” to make informed, executive
decisions in case of conflicts, or decide who gets to release what and when, in
order to minimise disruption over the chain of dependencies, this team was
hardly an entity capable of deciding the direction of project.</p>
<p>If effectively nobody is in charge, the direction of the project becomes an
emergent condition. People have a more or less vague sense of direction, and a
more or less defined end goal, so they will move towards it. Maybe not all at
the same time, and maybe not all on the same path; but the sum of all vectors is
not zero. Or, at least, it’s not zero all the time.</p>
<p>Of course, there are people that put more effort in order to balance the
equation, and those are the ones that we tend to recognise as “the leaders”, or,
in modern tech vernacular, “the rock stars”.</p>
<p>Seth Nickell and Calum Benson clearly fit that description. Both worked on
usability and design, and both worked on user testing—with Calum specifically
working on the professional workstation user given his position at Sun. Seth
was the <span class="caps">GNOME</span> Usability project lead, and alongside the rest of the usability
team, co-authored the <span class="caps">GNOME</span> Human Interface Guidelines, or <span class="caps">HIG</span>. The <span class="caps">HIG</span> was both
a statement of intent on the design direction of <span class="caps">GNOME</span>, as well as a checklist
for developers to go through and ensure that all the <span class="caps">GUI</span> applications would fit
into the desktop environment, by looking and behaving consistently. At the very
basic core of the <span class="caps">HIG</span> sat a few principles:</p>
<ol class="arabic simple">
<li>Design your application to let people achieve their goals</li>
<li>Make your application accessible to everyone</li>
<li>Keep the design simple, pretty, and consistent</li>
<li>Keep the user in control, informed on what happens, and forgive them for
any mistake</li>
</ol>
<p>These four tenets tried to move the needle of the design for <span class="caps">GNOME</span> applications
from the core audience of standard nerds to the world at large. In order to
design your application, you need to understand the audience that you wish to
target, and how to ensure you won’t get in their way; this also means you should
never limit your audience to people that are as able bodied as you are, or
coming from the same country or socio-economical background; your work should be
simple and reliable, in the sense that it doesn’t do two similar things in two
different ways; and that users should be treated with empathy, and never with
contempt. Even if you didn’t have access to the rest of the document, which
detailed how to deal with whitespace, or alignment, or the right widget for the
right action, you could already start working on making an application capable
of fitting in with the rest of <span class="caps">GNOME</span>.</p>
<p>Consistency and forgiveness in user interfaces also reflected changes in how
those interfaces should be configured—or if they should be configured at all.</p>
<p>In 2002 Havoc Pennington wrote what is probably the most influential essay on
how free and open source software user interface ought to be designed, called
“Free software <span class="caps">UI</span>”. The essay was the response to the evergreen question: “can
free and open source software methodology lead to the creation of a good user
interface?”, posed by Matthew Paul Thomas, a designer and volunteer contributor
at Mozilla.</p>
<p>Free and open source software design and usability suffer from various ailments,
most notably:</p>
<blockquote>
<ul class="simple">
<li>there are too many developers and not enough designers</li>
<li>designers can’t really submit patches</li>
</ul>
</blockquote>
<p>In an attempt at fixing these two issues the <span class="caps">GNOME</span> project worked on
establishing a design and usability team, with the help of companies to jump
start the effort; the presence of respected designers in leadership positions
also helped creating and fostering a culture where project maintainers would ask
for design review and testing. We’re still a bit far from asking for design
input instead of a design review, which usually comes with pushback now that the
code is in place. Small steps, I guess.</p>
<p>The important part of the essay, though, is on the cost of preferences—namely
that there’s no such thing as “just adding an option”.</p>
<p>Adding an option, on a technical level, requires adding code to handle all the
potential states of the option; it requires handling failure cases; a user
interface for setting and retrieving the option; it requires testing, and <span class="caps">QA</span>,
for all the states. Each option can interact with other options, which means a
combinatorial explosion of potential states, each with its own failure mode.
Options are optimal for a certain class of users, because they provide the
illusion of control; they are also optimal for a certain class of developers,
because they tickle the instinct of making general solutions to solve classes of
problems, instead of fixing the actual problem.</p>
<p>In the context of software released “as is”, without even the implied warranty
of being fit for purpose, like most free and open source software is, it removes
stress because it allows the maintainer from abdicating responsibility. It’s not
<strong>my</strong> fault that you frobnicated the foobariser and all your family photos have
become encoded versions of the <span class="caps">GNU</span> C library; you should have been holding a lit
black candle in your left hand and a curved blade knife in your right hand if
you didn’t want that to happen.</p>
<p>More than that, though: what you definitely don’t want is having preferences to
“fix” your application. If you have a bug, don’t add an option to work around
it. If somebody is relying on a bug for their workflow, then: too bad. Adding a
preference to work around a bug introduces another bug because now you encoded
a failure state directly into the behaviour of your code, and you cannot ever
change it.</p>
<p>Finally, settings should never leave you in an error state. You should never
have a user break their system just because they put invalid data in a text
field; or toggled the wrong checkbox; or clicked the wrong button. Recovering is
good, but never putting the user in the condition of putting bad values in the
system is the best approach because it is more resilient.</p>
<p>From a process standpoint, the development cycle of <span class="caps">GNOME</span> 1, and the release
process of <span class="caps">GNOME</span> 2.0, led to a complete overhaul of how the project should be
shepherded into releasing new versions. Releasing <span class="caps">GNOME</span> 2.0 led to many
compromises: features were not complete, known bugs ended up in the release
notes, and some of the underlying <span class="caps">API</span> provided by the development platform were
not really tested enough before freezing them for all time. It is hard to decide
when to shout “pencils down” when everyone is doing their own thing in their own
corner of the world. It’s even harder when you have a feedback loop between a
development platform that provides <span class="caps">API</span> for the rest of the platform, and needs
validation for the changes while they can still be made; and applications that
need the development platform to sit still for five minutes so that they can
be ported to the new goodness.</p>
<p><span class="caps">GNOME</span> was the first major free software project to switch away from a
feature-based development cycle and towards a time-based one, thanks to the
efforts of Jeff Waugh on behalf of the release team; the whole 2.0 development
cycle was schedule driven, with constant reminders of the various milestones and
freeze dates. Before the final 2.0 release, a full plan for the development
cycle was proposed to the community of maintainers; the short version of the
plan was:</p>
<blockquote>
The key elements of the long-term plan: a stable branch that is
extremely locked-down, an unstable branch that is always compilable
and dogfood-quality, and time-based releases at 6 month intervals.</blockquote>
<p>Given that the 2.0 release happened at the end of June, that would put the 2.2
release in December, which would have been problematic. Instead, it was decided
to have a slightly longer development cycle, to catch all the stragglers that
couldn’t make the cut for 2.0, and release 2.2 in February, followed by the 2.4
release in September. This period of adjustment led to the now familiar release
cadence of a March and a September releases every year. Time based releases and
freezing the features, <span class="caps">API</span>, translatable strings—and code, around the point-zero
release date—ensured that only features working to the satisfaction of the
maintainers, designers, and translators would end up in the hand of the users.
Or, at least, that was the general plan.</p>
<p>It’s important to note that all of these changes in the way <span class="caps">GNOME</span> as a community
saw itself, and the project they were contributing to, are probably the biggest
event in the history of the project itself—and, possibly, in the history of free
and open source software. The decision to focus on usability, accessibility, and
design, shaped the way people contributing and using <span class="caps">GNOME</span> think about <span class="caps">GNOME</span>; it
even changed the perception of <span class="caps">GNOME</span> for people not using it, for good or ill.
<span class="caps">GNOME</span>’s brand was solidified into one of care about design principles, and that
perception continues to this day. If something user visible changes in <span class="caps">GNOME</span> it
is <strong>assumed</strong> that design, usability, or accessibility had something to do with
it—even when it really didn’t; it is <strong>assumed</strong> that designers sat together,
did user studies, finalized a design, and then, in one of the less charitable
versions, lobbed it over the wall to module maintainers for its implementation
with no regard for objections.</p>
<p>That version of reality is so far removed from ours it might as well have
superheroes flying around battling monsters from other dimensions; and, yet,
the <span class="caps">GNOME</span> brand is so established that people will take it as an article of faith.</p>
<p>For all that, though, <span class="caps">GNOME</span> 2 <strong>did</strong> have usability studies conducted on it
prior to release; the Human Interface Guidelines were written in response to
those studies, and to established knowledge in the interaction design literature
and community; the changes in the system settings and the menu structures were
done after witnessing users struggle with the equivalent bits of <span class="caps">GNOME</span> 1. The
unnecessary settings that littered the desktop as a way to escape making
decisions, or as a way to provide some sort of intellectual challenge to the
developers were removed because, in the end, settings are not the goal for a
desktop environment that’s just supposed to launch applications and provide an
environment for those applications to exist.</p>
<p>This was peak <span class="caps">GNOME</span> brand.</p>
<p>On June 27, 2002, <span class="caps">GNOME</span> 2.0 was released. The <span class="caps">GNOME</span> community worked days and
nights for more than a year after releasing 1.4, and for more than two years
after releasing 1.2. New components were created, projects were ported,
documentation was written, screenshots were taken, text was translated.</p>
<p>Finally, the larger community of Linux users and enthusiasts would be able to
witness the result of all this amazing work, and their reaction was: thanks, I
hate it.</p>
<p>Well, no, that’s not really true.</p>
<p>Yes, a lot of people hated it—and they made damn well sure you knew that they
hated it. Mailing lists, bug trackers, articles and comments on news websites
were full of people angrily demanding their five clocks back; or their heavily
nested menu structure; or their millisecond-precision animation settings; or
their “Miscellanous” group of settings in a “Miscellaneous” tab of the control centre.</p>
<p>A lot of people simply sat in front of <span class="caps">GNOME</span> 2, and managed to get their work
done, before turning off the machine and going home.</p>
<p>A few people, though, saw something new; the potential of the changes, and of
the focus of the project. They saw beyond the removed configuration options;
the missing features left for a future cycle; the bugs caused by the massive
changes in the underlying development platform.</p>
<p>Those few people were the next generation of contributors to <span class="caps">GNOME</span>; new
developers, sure, but also new designers; new documentation writers; new
translators; new artists; new maintainers. They were inspired by the newly
refocused direction of project, by its ethos, to the point of deciding to
contribute to it. <span class="caps">GNOME</span> needed to be ready for them.</p>
<hr class="docutils" />
<p>Next week the magic and shine of the release starts wearing off, and we’re back
to flames and long discussions on features, media stacks, inclusion of
applications in the release, and what happens when Novell decides to go on a
shopping spree, in the episode titled “Honeymoon phase”.</p>
<div class="section" id="references">
<h2>References</h2>
<ul class="simple">
<li><a class="reference external" href="https://www.bassi.io/category/history/podcast.xml">Subscribe</a> to this podcast</li>
<li>Music: “<a class="reference external" href="http://freemusicarchive.org/music/Lee_Rosevere/Music_For_Podcasts/Lee_Rosevere_-_Music_For_Podcasts_-_04_Looking_Back">Looking Back</a>“, by Lee Rosevere - <span class="caps">CC</span> <a class="reference external" href="https://creativecommons.org/licenses/by/4.0/">by-4.0</a></li>
<li>Logo: The History of <span class="caps">GNOME</span>, by <a class="reference external" href="http://jimmac.musichall.cz/">Jakub Steiner</a></li>
<li>This podcast is released under a <a class="reference external" href="https://creativecommons.org/licenses/by-sa/4.0/">Creative Commons license</a></li>
<li><a class="reference external" href="https://www.bassi.io/articles/2018/10/25/table-of-contents/">Table of contents</a>.</li>
<li><a class="reference external" href="https://mail.gnome.org/archives/gnome-announce-list/2002-June/msg00111.html"><span class="caps">GNOME</span> 2.0 Desktop and Developer Platform Released!</a>.</li>
<li><a class="reference external" href="http://web.archive.org/web/20030415115847/http://developer.gnome.org/projects/gup/hig/1.0/usabilityprinciples.html#design-for-people"><span class="caps">GNOME</span> Human Interface Guidelines (1.0) [archived]</a>.</li>
<li><a class="reference external" href="https://ometer.com/free-software-ui.html">Free Software <span class="caps">UI</span></a>.</li>
<li><a class="reference external" href="http://web.archive.org/web/20020428015130/http://mpt.phrasewise.com/2002/04/13">Why Free Software usability tends to suck [archived]</a>.</li>
<li><a class="reference external" href="https://mail.gnome.org/archives/gnome-hackers/2002-June/msg00041.html">Proposed release process/plans</a>.</li>
</ul>
</div>
Episode 2.1: On Brand2019-01-24T16:00:00+00:002019-02-24T19:18:26+00:00ebassitag:www.bassi.io,2019-01-24:/articles/2019/01/24/history-of-gnome-episode-2-1/<p class="first last">A wild <span class="caps">GTK</span> major <span class="caps">API</span> change appears! It uses better text and icon rendering. It’s super effective! <span class="caps">GNOME</span> dons flame retardant pants. It’s not very effective!</p>
<p>In the beginning of the year 2000, with the 1.2 release nearly out of the door,
the <span class="caps">GNOME</span> project was beginning to lay down the groundwork for the design and
development of the 2.0 release cycle. The platform had approached a good level
of stability for the basic, day to day use; whatever rough edges were present,
they could be polished in minor releases, while the core components of the
platform were updated and transitioned incrementally towards the next major
version. Before mid-2000, we already started seeing a 1.3 branch for <span class="caps">GTK</span>, and
new libraries like Pango and GdkPixbuf were in development to provide advanced
text rendering and replace the old and limited imlib image loading library,
respectively. Of course, once you get the ball rolling it’s easy to start piling
features, refactorings, and world breaking fixes on top of each other.</p>
<p>If X11 finally got support for loading True Type fonts, then we would need a
better <span class="caps">API</span> to render text. If we got a better <span class="caps">API</span> to render text, we would need
a better <span class="caps">API</span> to compose Unicode glyphs coming from different writing systems,
outside of the plain Latin set.</p>
<p>If we had a better image loading library, we could use better icons. We could
even use <span class="caps">SVG</span> icons all over the platform, instead of using the aging <span class="caps">XPM</span> format.</p>
<p>By May 2000, what was supposed to be <span class="caps">GTK</span> 1.4 turned into the development cycle
for the first major <span class="caps">API</span> break since the release of <span class="caps">GTK</span> 1.0, which happened just
the year before. Of course, the 1.2 cycle would continue, and would now cover
not just <span class="caps">GNOME</span> 1.2, but the 1.4 release as well.</p>
<p>It wasn’t <span class="caps">GTK</span> itself that led the charge, though. In a way, breaking <span class="caps">GTK</span>’s <span class="caps">API</span>
was the side effect of a much deeper change.</p>
<p>As we’ve seen in the first chapter’s side episode on <span class="caps">GTK</span>, GLib was a simple C
utility library for the benefit of <span class="caps">GTK</span> itself; the type system for writing
widgets and other toolkit-related data using object orientation was part of <span class="caps">GTK</span>,
and required depending on <span class="caps">GTK</span> for writing any type of object oriented C. It
turns out that various C projects in the <span class="caps">GNOME</span> ecosystem—projects like GdkPixbuf
and Pango—liked the idea of having a type system written in C and, more
importantly, the ability to easily build language bindings for any <span class="caps">API</span> based on
that type system. Those projects didn’t really need, or want, a dependency on a
<span class="caps">GUI</span> toolkit, with its own dependency on image loading libraries, or windowing
systems. Moving the type system to a lower level library, and then reusing it in
<span class="caps">GTK</span> would have neatly solved the problem, and made it possible to create a
semi-standard C library shared across various projects.</p>
<p>Of course, since no solution survives contact with software developers, people
depending on GLib for basic data structures, like a hash table, didn’t want to
suddenly have a type system as well. For that reason, GLib acquired a second
shared library containing the <span class="caps">GTK</span> type system, upgraded and on steroid; the
“signal” mechanism to invoke a named list of functions, used by <span class="caps">GTK</span> to deliver
windowing system events; and a base object class, called GObject, to replace
<span class="caps">GTK</span>’s own GtkObject. On top of that, GObject provided properties associated to
object instances; interface types; dynamic types for loadable modules; type
wrappers for plain old data types; generic function wrappers for language
bindings; and a richer set of memory management semantics.</p>
<p>Another important set of changes finding their way down to GLib was the
portability layer needed to make sure that <span class="caps">GTK</span> applications could run on both
Unix-like, and non-Unix-like operating systems. Namely, Windows, for which <span class="caps">GTK</span>
was getting a backend, alongside additional backends for BeOS, macOS, and direct
framebuffer rendering. The Windows backend for <span class="caps">GTK</span> was introduced to make <span class="caps">GIMP</span>
build and work on that platform, and increase its visibility, and possibly the
amount of contributions—something that free and open source software communities
always strive towards, even if it does increase the amount of feature request,
bug reports, and general work for the maintainers. It’s important to note that
<span class="caps">GTK</span> wasn’t suddenly becoming a cross-platform toolkit, meant to be used to write
applications targeting multiple platforms; the main goal was always to allow extant
Linux applications to be easily ported to other operating systems first, and
write native, non-Linux applications as a distant second.</p>
<p>With a common, low level <span class="caps">API</span> provided by GLib and GObject, we start to see
the beginning of a more comprehensive software development platform for <span class="caps">GNOME</span>;
if all the parts of the platform, even the ones that are not directly tied to
the windowing system, follow the same semantics when it comes to memory
management, type inheritance, properties, signals, and coding practices, then
writing documentation becomes easier; developing bindings for various
programming languages becomes a much more tractable problem; creating new, low
level libraries for, say, sending and receiving data from a web server, and
leveraging the existing community becomes possible. With the release of GLib
and <span class="caps">GTK</span> 2.0, the <span class="caps">GNOME</span> software development platform moves from a collection
of libraries with a bunch of utilities built on top of <span class="caps">GTK</span> to a comprehensive
set of functionality centered on GLib and GObject, with <span class="caps">GTK</span> as the <span class="caps">GUI</span> toolkit,
and a collection of libraries fullfilling specific tasks to complement it.</p>
<p>Of course, that still means having libraries like libgnome and libgnomeui lying
around, as a way to create <span class="caps">GNOME</span> applications that integrate with the <span class="caps">GNOME</span>
ecosystem, instead of just <span class="caps">GTK</span> applications. <span class="caps">GTK</span> gaining more <span class="caps">GNOME</span>-related
features, or more <span class="caps">GNOME</span>-related integration points, was a source of contention
inside the community. <span class="caps">GTK</span> was considered by some <span class="caps">GNOME</span> contributors to be
a “second party” project; it had its own release schedule, and its own release
manager; it incorporated feedback from <span class="caps">GNOME</span> application and library developers,
but it was also trying to serve non-<span class="caps">GNOME</span> communities, by providing a useful
generic <span class="caps">GUI</span> toolkit out of the box. On the other side of the spectrum, some <span class="caps">GNOME</span>
developers wanted to keep the core as lean as possible, and accrue functionality
inside the <span class="caps">GNOME</span> libraries, like libgnome and libgnomeui, even if those
libraries were messier and didn’t receive as much scrutiny as GLib and <span class="caps">GTK</span>.</p>
<p>Most of 2001 was spent developing GObject and Pango, with the latter proving to
be one of the lynchpins of the whole platform release. As we’ve seen in episode
1.5, Pango provided support for complex, non-latin text, a basic requirement for
creating <span class="caps">GUI</span> applications that could be used outside of the <span class="caps">US</span> and Europe; in
order to get there, though, various pieces of the puzzle had to come together first.</p>
<p>The first, big piece, was adding the ability for applications to render TrueType
fonts on X11, using fontconfig to configure, enumerate, and load the fonts
installed in a system, and the Xft library, for rendering glyphs. Before Xft, X
applications only had access to core bitmap fonts, which may look impressive if
all you have a is thin terminal in 1987, but compared to the font rendering
available on Windows and macOS they were already painfully out of date by about
10 years. During the <span class="caps">GNOME</span> 1.x cycle some components with custom rendering code
already started using Xft directly, instead of going through <span class="caps">GTK</span>’s text
rendering wrappers around X11’s core <span class="caps">API</span>; this led to the interesting result of,
for instance, the text rendering in Nautilus pre-1.0 looking miles better than
every other <span class="caps">GTK</span> 1 application, including the rest of the desktop components.</p>
<p>The other big piece of the puzzle was Unicode. Up until <span class="caps">GTK</span> 2.0, all text inside
<span class="caps">GTK</span> applications was pretty much passed as it was to the underlying windowing
system text rendering primitives; that mostly meant either <span class="caps">ASCII</span>, or one of the
then common <span class="caps">ISO</span> encodings; this not only imposed restrictions on what kind of
text could be rendered, but it also introduced additional hilarity when it came
to running applications localized by somebody in Europe on a computer in the
<span class="caps">US</span>, or in Russia, or in Japan.</p>
<p>Taking advantage of Unicode to present text meant adding various pieces of <span class="caps">API</span>
to GLib, mostly around the Unicode tables, text measurement, and iteration. More
importantly, though, it meant changing all text used inside <span class="caps">GTK</span> and <span class="caps">GNOME</span>
applications to <span class="caps">UTF</span>-8. It meant that file system paths, translations, and data
stored on non-Unicode systems had to be converted—if the original encoding was
available—or entirely rewritten, if you didn’t want your <span class="caps">GUI</span> to be a tragedy of
unintelligible text.</p>
<p>If the written word was in the process of being transformed to another format,
pictures were not in a better position. The state of the art for raster images
in a <span class="caps">GTK</span> application was still <span class="caps">XPM</span>, a bitmap format that was optimised for
storage inside C sources, and compiled with the rest of the application. Sadly,
the results were less than stellar, compared to more common formats like <span class="caps">JPEG</span>
and <span class="caps">PNG</span>; additionally, the main library used to read image assets, imlib, was
very much outdated, and mostly geared towards loading image data into X11
pixmaps. Imlib provided entry points for integrating with the <span class="caps">GTK</span> drawing <span class="caps">API</span>,
but it was a separate project, part of the Enlightenment window manager—which
was already moving to its replacement, imlib2. The work to replace imlib with a
new <span class="caps">GNOME</span>-driven library, called GdkPixbf, began in 1999, and was merged into
the <span class="caps">GTK</span> repository as a way to provide <span class="caps">API</span> to load images in various formats
like <span class="caps">PNG</span>, <span class="caps">GIF</span>, <span class="caps">JPEG</span>, and Windows <span class="caps">BMP</span> and <span class="caps">ICO</span> files directly into <span class="caps">GTK</span> widgets.
As an additional feature, GdkPixbuf had an extensible plugin system which
allowed writing out of tree modules for loading image formats without
necessarily adding new dependencies to <span class="caps">GTK</span>. Another feature of GdkPixbuf was
a transformation and compositing <span class="caps">API</span>, something that imlib did not provide.</p>
<p>All in all, it took almost 2 years of development for <span class="caps">GTK</span> 1.3 to turn into <span class="caps">GTK</span>
2.0, with the first stable releases of GLib, Pango, and <span class="caps">GTK</span> cut in March 2002,
after a few months of feature freeze needed to let <span class="caps">GNOME</span> and application
developers catch up with the changes, and port their projects to the new <span class="caps">API</span>.
The process of porting went without a hitch, with everyone agreeing that the new
functionality was incredibly easy to use, and much better than what came before.
Developers were so eager to move to the new libraries that they started doing so
during the development cycle, and constantly kept up with the changes so that
every source code change was just a few lines of code.</p>
<p>Yes, <em>of course</em> I’m lying.</p>
<p>Porting proceeded in fits and jumps, and it was either done at the very
beginning, when the difference between major versions of the libraries were
minimal and gave a false impression of what the job entailed; or it happened at
the very end of the cycle, with constant renegotiation of every single change
in the platform, a constant barrage of questions of why platform developers were
trying to impose misery on the poor application developers, why won’t you think
of the application developers…</p>
<p>Essentially, like every single major version change in every project, ever.</p>
<p>On top of the usual woes of porting, we have to remember that <span class="caps">GNOME</span> 1.4 started
introducing technology previews for <span class="caps">GNOME</span> 2.0, like GConf, the new configuration
storage. While Havoc Pennington had written GConf between 2000 and 2001, and
concentrated mostly on the development of <span class="caps">GTK</span> 2 after that, it was now time to
start using GConf as part of the desktop itself, as a replacement for the
configuration storage used by components by the Panel, and by applications using
libgnome, which is when things got heated.</p>
<p>Ximian developer Dieter Maurer thought that some of the trade-offs of the GConf
implementation, mostly related to its use of <span class="caps">CORBA</span>’s type system, were enough of
a road block that they decided to write their own configuration client <span class="caps">API</span>,
called bonobo-config, which re-implemented the GConf design with a more
pervasive use of <span class="caps">CORBA</span> types and interfaces, thanks to the libbonobo work that
was pursued as part of the <span class="caps">GNOME</span> componentisation effort. Bonoboc-config had
multiple backends, one of which would wrap GConf, as, you might have guessed it
already, a way to route around a strongly opinionated maintainer, working for a
different company.</p>
<p>The last bit is probably the first real instance of a massive flame war caused
by strife between commercial entities vying for the direction of the project.</p>
<p>The flames started off as a disagreement in design and technical direction
between GConf and bonobo-config maintainers, confined to the GConf development
mailing list, but soon spiralled out of control once libgnome was changed to use
bonobo-config, engulfing multiple mailing list in fires that burned brighter
than a thousand suns. Accusations of attempting to destroy the <span class="caps">GNOME</span> projects
flew through the air, followed by rehashing of every single small roadblock ever
put in the way of each interested party. The newly appointed release manager for
<span class="caps">GNOME</span> 2.0 and committer of the dependency on bonobo-config inside libgnome,
Martin Baulig, dramatically quit his role and left the community (only to come
back a bit later), leaving Anders Carlsson to revert the controversial
change—followed by a new round of accusations.</p>
<p>Totally normal community interactions in a free software project.</p>
<p>In the end, concessions were made, hatred simmered, and bonobo-config was left
behind, to be used only be applications that decided to opt into its usage, and
even then it was mostly used as a wrapper around GConf.</p>
<hr class="docutils" />
<p><span class="dquo">“</span>Fonts, Unicode, and icons” seems like a small set of user-visibile features for
a whole new major version of a toolkit, and desktop environment. Of course, that
byline kind of ignores all the work laid down behind the scenes, but end users
don’t care about that, right? We’ll see what happens when a major refactoring to
pay down the technical debt accrued over the first 5 years of <span class="caps">GNOME</span>, and clear
the rubble to build something that didn’t make designers, documentation writers,
and <span class="caps">QA</span> testers cry tears of blood, in the next episode, “Release Day”, which
will be out in <strong>two</strong> weeks time, as next week I’m going to be at the <span class="caps">GTK</span>
hackfest, trying to pay down the technical debt accrued over the 3.0 development
cycle of the toolkit. I’m sure it’ll be fine, this time. It’s fine. We’re going
to be fine. It’s fine. We’re fine.</p>
<div class="section" id="references">
<h2>References</h2>
<ul class="simple">
<li><a class="reference external" href="https://www.bassi.io/category/history/podcast.xml">Subscribe</a> to this podcast</li>
<li>Music: “<a class="reference external" href="http://freemusicarchive.org/music/Lee_Rosevere/Music_For_Podcasts/Lee_Rosevere_-_Music_For_Podcasts_-_04_Looking_Back">Looking Back</a>“, by Lee Rosevere - <span class="caps">CC</span> <a class="reference external" href="https://creativecommons.org/licenses/by/4.0/">by-4.0</a></li>
<li>Logo: The History of <span class="caps">GNOME</span>, by <a class="reference external" href="http://jimmac.musichall.cz/">Jakub Steiner</a></li>
<li>This podcast is released under a <a class="reference external" href="https://creativecommons.org/licenses/by-sa/4.0/">Creative Commons license</a></li>
<li><a class="reference external" href="https://www.bassi.io/articles/2018/10/25/table-of-contents/">Table of contents</a>.</li>
</ul>
</div>
Episode 2.0: Retrospective2019-01-17T14:45:00+00:002019-02-24T19:18:18+00:00ebassitag:www.bassi.io,2019-01-17:/articles/2019/01/17/history-of-gnome-episode-2-0/<p class="first last">A retrospective on <span class="caps">GNOME</span> 1.x, before we launch into the main narrative of <span class="caps">GNOME</span> 2; we’re going to look back at what <span class="caps">GNOME</span> 1 did right; what it did wrong; and what it meant in the larger context of the history of the project</p>
<p>Hello, everyone, and welcome back to the History of <span class="caps">GNOME</span>! If you’re listening
to this in real time, I hope you had a nice break over the end of 2018, and are
now ready to begin the second chapter of our main narrative. I had a lovely time
over the holidays, and I’m now back working on both the <span class="caps">GNOME</span> platform and on
the History of <span class="caps">GNOME</span>, which means reading lots of code by day, and reading lots
of rants and mailing list archives in the evening—plus, the occasional hour or
so spent playing videogames in order to decompress.</p>
<p>Before we plunge right back into the history of <span class="caps">GNOME</span> 2, though, I wanted to
take a bit of your time to recap the first chapter, and to prepare the stage for
the second one. This is a slightly opinionated episode… Well, slightly more
opinionated than usual, at least… As I’m trying to establish the theme of the
first chapter as a starting point for the main narrative for the future. Yes,
this is an historical perspective on the <span class="caps">GNOME</span> project, but history doesn’t
serve any practical purposes if we don’t glean trends, conflicts, and
resolutions of past issues that can apply to the current period. If we don’t
learn anything from history then we might as well not have any history at all.</p>
<p>The first chapter of this history of the <span class="caps">GNOME</span> project covered roughly the four
years that go from Miguel de Icaza’s announcement in August 1997 to the release
of <span class="caps">GNOME</span> 1.4 in April 2001—and we did so in about 2 hours, if you count the
three side forays on <span class="caps">GTK</span>, language bindings, and applications that closed the
first block of episodes.</p>
<p>In comparison, the second chapter of the main narrative will cover the 9 years
of the 2.x release cycles that went from 2001 to 2010. The duration of the
second major cycle of <span class="caps">GNOME</span> is not just twice as long as the first, it’s also
more complicated, as a result of the increased complexity for any project that
deals with creating a modern user experience—one not just for the desktop but
also for the mobile platforms that were suddenly becoming more important in the
consumer products industry, as we’re going to see during this chapter. The
current rough episode count is, at the moment I’m reading this, about 12, but as
I’m striving to keep the length of each episode in the 15 to 20 minutes range,
I’m not entirely sure how many actual episodes will make up the second chapter.</p>
<p>Looking back at the beginning of the project we can say with relative certainty
that <span class="caps">GNOME</span> started as a desktop environment in a time when desktops were simpler
than they are now; at the time of its inception, the bar to clear was
represented by Windows 95, and while it was ostensibly a fairly high bar to
clear for any volunteer-driven effort, by the time <span class="caps">GNOME</span> 1.4 was released to the
general public of Linux enthusiasts and Unix professionals, it was increasingly
clear that a new point of comparison was needed, mostly courtesy of Apple’s <span class="caps">OS</span> X
and Microsoft’s Windows <span class="caps">XP</span>. Similarly, the hardware platforms started off as
simpler iterations over the <span class="caps">PC</span> compatible space, but vendors quickly moved the
complexity further and further into the software stack—like anybody with a
WinModem in the late ‘90s could tell you. Since Linux was a blip on the radars
of even the most widespread hardware platforms, new hardware targeted Windows
first and foremost, and support for Linux appeared only whenever some
enterprising volunteer would manage to reverse engineer the chipset <em>du jour</em>,
if it appeared at all.</p>
<p>As we’ve seen in the first episode of the first chapter, the precursors to what
would become a “desktop environment” in the modern sense of the term were made
of smaller components, bolted on top of each other according to the needs, and
whims, of each user. A collection of <span class="caps">LEGO</span> bricks, if you will, if only the
bricks were made by a bunch of different vendors and you had to glue them
together to build something. <span class="caps">KDE</span> was the very first environment for Linux that
tried to mandate a more strict integration between its parts, by developing and
releasing all if its building blocks as comprehensive archives. <span class="caps">GNOME</span> initially
followed the same approach, with libraries, utilities, and core components
sharing the same <span class="caps">CVS</span> repositories, and released inside shared distribution
archives. Then, something changed inside <span class="caps">GNOME</span>; and figuring out what changed
is central to understanding the various tensions inside a growing free and open
source software project.</p>
<p>If desktop environments are the result of a push towards centralisation, and
comprehensive, integrated functionality exposed to the people using, but not
necessarily contributing to them, splitting off modules into their own
repositories, using their own release schedules, their own idiosynchrasies in
build systems, options, coding styles, and contribution policies, ought to run
counter to that centralising effort. The decentralisation creates strife between
projects, and between maintainers; it creates modularisation and <span class="caps">API</span> barriers;
it generates dependencies, which in turn engender the possiblity of conflict,
and barriers to not just contribution, but to distribution and upgrade.</p>
<p>Why, then, this happens?</p>
<p>The mainstream analytical framework of free and open source software tells us
that communities consciously end up splitting off components, instead of
centralising functionality, once it reaches critical mass; community members
prefer delegation and composition of components with well-defined edges and
interactions between them, instead of piling functionality and <span class="caps">API</span> on top of a
hierarchy of poorly defined abstractions. They like small components because
maintainers value the design philosophy that allows them to provide choice to
people using their software, and gives discerning users the ability to compose
an operating system tailored to their needs, via loosely connected interfaces.</p>
<p>Of course, all I said above is a complete and utter fabrication.</p>
<p>You have no idea of the amounts of takes I needed to manage to get through
all of that without laughing.</p>
<p>The actual answer would be Conway’s Law:</p>
<blockquote>
organizations which design systems […] are constrained to produce designs
which are copies of the communication structures of these organizations</blockquote>
<p>We have multiple contributors, typically highly opinionated, typically young
or, at least, without lots of real world experience. Worse case, the only
experience available comes from years of computer science lessons, where object
orientation reigns supreme, and it’s still considered a good idea despite all
the evidence to the contrary.</p>
<p>These multiple contributors end up carving their own spaces, because the
required functionality is large, and the number of people working on it is
always smaller than a 100% coverage. New functionality is added; older
modules are dropped because “broken”, or “badly designed”; new dependencies
are created to provide shared functionality, or introduced as abstraction layers
to paper over multiple modules offering sligthly different takes on how some
functionality ought to be implemented, or what kind of dependencies they
require, or what kind of language or licensing terms ought to be used.</p>
<p>Complex free software projects with multiple contributors working on multiple
components, favour smaller modules because it makes it easier for each
maintainer to keep stuff in their head without going stark raving mad. Smaller
modules make it easier to insulate a project against strongly opinionated
maintainers, and let other, strongly opinionated maintainers, route around the
things they don’t like. Self-contained modules make niche problems tractable,
or at least they contain the damage.</p>
<p>Of course, if we declared this upfront, it would make everybody’s life easier as
it would communicate a clear set of expectations; it would, on the other hand,
have the side effect of revealing the wardrobe malfunction of the emperor, which
means we have to dress up this unintended side effect of Conway’s Law as “being
about choice”, or “mechanism, not policy”, or “network object model”.</p>
<p>The first chapter in the history of the <span class="caps">GNOME</span> project can be at least partially
interpreted within this framework; the idea that you can take a complex problem
space and partition it until each issue becomes tractable individually, and then
build up the solution out of the various bits and pieces you managed to solve,
letting it combine and recombine as best as it can to suit the requirements of
the moment, platform, or use case. Throw in <span class="caps">CORBA</span> as an object model for good
measure, and you end up with a big box of components that solve arbitrarily
small issues on their own, and that can theoretically scale upwards in
complexity. This, of course, ignores the fact that combinatorial explosions of
interactions make things very interesting for anybody developing, testing, and
using these components—and I use “interesting” in the “oh god oh god we’re all
going to die” sense of the word.</p>
<p>More importantly, and on a social level, this framework allows project
maintainers to avoid having to make a decision on what should work and what
shouldn’t; what is supported and what isn’t; and even what is part of the
project and what falls outside of it. If there some part of the stack that is
misbehaving, wrap it up; even better, if there are multiple competing
implementations, you can always paper over them with an abstraction layer. As
long as the <span class="caps">API</span> surface is well defined, functionality is somebody else’s
problem; and if something breaks, or mysteriously doesn’t work, then I’m sure
the people using it are going to be able to fix it.</p>
<p>Well, it turns out that all the free software geeks capable of working on a
desktop environment are <em>already</em> working on one, which by definition means
that they are the only ones that can fix the issues they introduced.</p>
<p>Additionally, and this is a very important bit that many users of free and open
source software fail to grapple with: volunteer work is not fungible—that is,
you cannot tell people doing things on their spare time, and out of the goodness
of their hearts, to stop doing what they are doing, and volunteer on something
else. People just don’t work that way.</p>
<p>So, if “being about choice” is on the one end of the spectrum, what’s at the
other? Maybe a corporate-like structure, with a project driven by the vision of
a handful of individuals, and implemented by everyone else who subscribes to
that vision—or, at least, that gets paid to implement it.</p>
<p>Of course, the moment somebody decides to propose their vision, or work to
implement it, or convince people to follow it, is the moment when they open
themselves up to criticism. If you don’t have a foundational framework for your
project, nobody can accuse you of doing something wrong; if you do have it,
though, then the possibilities fade away, and what’s left is something tangible
for people to grapple with—for good or ill.</p>
<p>At the beginning of the <span class="caps">GNOME</span> project we had very few individuals, with a vision
for the desktop; while it was a vision made of components interoperating to
create something flexible and adaptable to various needs, it still adhered to
specific design goals, instead of just putting things together from disparate
sources, regardless of how well the interaction went. This led to a foundational
period, where protocols and interfaces were written to ensure that the
components could actually interoperate, which led to a somewhat lacklustre
output; out of three 1.x minor releases all we got was a panel, a bunch of clock
applets, and a control centre. All the action happened on the lower layers of
the stack. <span class="caps">GTK</span> became a reasonably usable free software <span class="caps">GUI</span> toolkit for Linux
and other Unix-like operating systems; the X11 world got a new set of properties
and protocols to deal with modern workflows, in the form of the <span class="caps">EWMH</span>;
applications and desktop modules got shared <span class="caps">UI</span> components using <span class="caps">CORBA</span> to
communicate between them.</p>
<p>On a meta level, the <span class="caps">GNOME</span> project established a formal structure on itself,
with the formation of a release team and a non-profit foundation that would work
as a common place to settle the internal friction between maintainers, and the
external contributions from companies and the larger free and open source
software world.</p>
<p>Going back to our frame of reference to interpret the development of <span class="caps">GNOME</span> as a
community of contributors, we can see this as an attempt to rein in the
splintering and partition of the various components of the project, and as a
push towards its new chapter. This tension between the two efforts—one to create
an environment with a singular vision, even if driven by multiple people; and
the other, to create a flexible environment that respected the various domains
of each individual maintainer, if not each individual user—defined the first
major cycle, as it would (spoiler alert) every other major cycle.</p>
<p>Now that the foundational period was over, though, and the challenges provided
by commercial platforms like Windows and <span class="caps">OS</span> X had been renewed, the effort to
make <span class="caps">GNOME</span> evolve further was not limited to releasing version 2.0, but to
establish a roadmap for the future beyond it.</p>
<hr class="docutils" />
<p>Next week we’re going to dive right back into the development of <span class="caps">GNOME</span>, starting
with the interregnum period between 1.4 and 2.0, in which our plucky underdogs
had finally become mainstream enough to get on Sun and <span class="caps">IBM</span> radars, and had to
deal with the fact that <span class="caps">GNOME</span> was not just a hobby any more, in the episode
titled: “On Brand”.</p>
<div class="section" id="references">
<h2>References</h2>
<ul class="simple">
<li><a class="reference external" href="https://www.bassi.io/category/history/podcast.xml">Subscribe</a> to this podcast</li>
<li>Music: “<a class="reference external" href="http://freemusicarchive.org/music/Lee_Rosevere/Music_For_Podcasts/Lee_Rosevere_-_Music_For_Podcasts_-_04_Looking_Back">Looking Back</a>“, by Lee Rosevere - <span class="caps">CC</span> <a class="reference external" href="https://creativecommons.org/licenses/by/4.0/">by-4.0</a></li>
<li>Logo: The History of <span class="caps">GNOME</span>, by <a class="reference external" href="http://jimmac.musichall.cz/">Jakub Steiner</a></li>
<li>This podcast is released under a <a class="reference external" href="https://creativecommons.org/licenses/by-sa/4.0/">Creative Commons license</a></li>
<li><a class="reference external" href="https://www.bassi.io/articles/2018/10/25/table-of-contents/">Table of contents</a>.</li>
<li><a class="reference external" href="https://en.wikipedia.org/wiki/Conway's_law">Conway’s Law</a>.</li>
</ul>
</div>
And I’m home2018-12-14T19:00:00+00:002018-12-14T19:26:22+00:00ebassitag:www.bassi.io,2018-12-14:/articles/2018/12/14/and-im-home/<p>In which I talk about life this past year, and changing jobs from Endless to the <span class="caps">GNOME</span> Foundation</p><p>It’s almost the end of the year, so it’s time for a recap of the previous
episodes, I guess.</p>
<p>The <em>tl;dr</em> of 2018: started pretty much the same; massive dip in the
middle; and, finally, got better at the very end.</p>
<p>The first couple of months of the year were pretty good; had a good time at
the <span class="caps">GTK</span> hackfest and <span class="caps">FOSDEM</span>, and went to the Recipes hackfest in Yogyakarta
in February.</p>
<p>In March, my wife Marta was diagnosed with breast cancer; Marta already had
(different types of) cancer twice in her life, and had been in full
remission for a couple of years, which meant she was able to cope with the
mechanics of the process, but it was still a solid blow. Since she had
already gone through a round of radiotheraphy 20 years ago—which had likely
a hand in the cancer appearing now—her only option was surgery to remove the
whole breast tissue and the associated lymph nodes. Not fun, but surgery
went well, and she didn’t even need chemotherapy, so all in all it could
have been way, way worse.</p>
<p>While Marta and I were dealing with that, I suddenly found myself out of a
job, after working five years at Endless.</p>
<p>To be fair, this left me with enough time to help out Marta while she was
recovering—which is why I didn’t come to <span class="caps">GUADEC</span>. After Marta was back on her
feet, and was able to raise her right arm above her head, I took the first
vacation in, I think, about four years. I relaxed, read a bunch of books,
played some video games, built <a href="https://www.instagram.com/p/Bq7R5HfhjR2/">many</a>, <a href="https://www.instagram.com/p/BppihJyBEMq/">many</a>,
<a href="https://www.instagram.com/p/Bm0hDJGD6Ki/"><strong>many</strong></a> Gundam plastic models, recharged my batteries—and ended
up finally having time to spend on <a href="https://www.bassi.io/articles/2018/10/25/history-of-gnome-episode-0/">a project that I had pushed back for a
while</a>, because I really needed to add writing and producing 15 to 20
minutes of audio every week, after perusing thousands of email archives and
old web pages on the <a href="http://web.archive.org/">Wayback Machine</a>. Side note: donate to
the Wayback Machine, if you can. They provide a fundamental service for
everybody using the Web, and especially for people like me who want to trace
the history of things that happen on the Web.</p>
<p>Of course I couldn’t stay home playing video games, recording podcasts, and
building gunplas forever, and so I had to figure out where to go to work
next, as I do enjoy being able to have a roof above my head, as well as
buying food and stuff. By a crazy random happenstance, the <span class="caps">GNOME</span> Foundation
announced that, thanks to a generous anonymous donation, it would start
hiring staff, and that one of the open positions was for a <span class="caps">GTK</span> developer. I
decided to apply, as, let’s be honest, it’s basically <strong>the</strong> dream job for
me. I’ve been contributing to <span class="caps">GNOME</span> components for about 15 years, and to
<span class="caps">GTK</span> for 12; and while I’ve been paid to contribute to some <span class="caps">GNOME</span>-related
projects over the years, it was always as part of non-<span class="caps">GNOME</span> related work.</p>
<p>The hiring process was really thorough, but in the end I managed to land the
most amazing job I could possibly hope for.</p>
<p>If you’re wondering what I’ll be working on, here’s a rough list:</p>
<ul>
<li>improving performance, especially on less powerful devices</li>
<li>identify and land new features</li>
<li>identify and fix pain points for current consumers of <span class="caps">GTK</span></li>
</ul>
<p>On top of that, I’ll try to do my best to increase the awareness of the work
being done on both the <span class="caps">GTK</span> 3.x stable branch, and the 4.x development
branch, so expect more content appearing on the development blog.</p>
<p>The overall idea is to ensure that <span class="caps">GTK</span> gets more exposure and mindshare in
the next 5 years as the main toolkit for Linux and Unix-like operating
systems, as well better functionality for application developers that want
to make sure their projects work on other platforms.</p>
<p>Finally, we want to make sure that more people feel confident enough to
contribute to the core application development platform; if you have your
pet feature or your pet bug inside <span class="caps">GTK</span>, and you want guidance, feel free
to reach out to me.</p>
<hr>
<p>Hopefully, the next year will <strong>not</strong> look like this one, and will be a
bit better. Of course, if we in the <span class="caps">UK</span> don’t all die in the fiery chaos
that is the Brexit circus…</p>Episode 1.c: Applications2018-12-14T18:00:00+00:002019-02-24T19:18:11+00:00ebassitag:www.bassi.io,2018-12-14:/articles/2018/12/14/history-of-gnome-episode-1-c/<p class="first last">A side episode! Early applications in the <span class="caps">GNOME</span> 1.x era</p>
<p>First of all, I’d like to apologise for the lateness of this episode. As you
may know, if you follow me on social media, I’ve started a new job as <span class="caps">GTK</span> core
developer for the <span class="caps">GNOME</span> Foundation—yes, I’m actually working at my dream job,
thank you very much. Of course this has changed some of the things around my
daily schedule, and since I can only record the podcast when ambient noise
around my house is not terrible, something had got to give. Again, I apologise,
and hopefully it won’t happen again.</p>
<hr class="docutils" />
<p>Over the course of the first chapter of the main narrative of the history of
the <span class="caps">GNOME</span> project, we have been focusing on the desktop and core development
platform produced by <span class="caps">GNOME</span> developers, but we did not really spend much time
on the applications—except when they were part of the more “commercial” side
of things, like Evolution and Nautilus.</p>
<p>Looking back at Miguel’s announcement, though, we can see “a complete set of
user friendly applications” in the list of things that the <span class="caps">GNOME</span> project
would be focusing on. What good a software development platform and
environment are, if you can’t use them to create and run applications?</p>
<p>While <span class="caps">GIMP</span> and <span class="caps">GNOME</span> share a great many things, it’s hard to make the case
for the image manipulation program to be part of <span class="caps">GNOME</span>; yes: it’s hosted on
<span class="caps">GNOME</span> infrastructure, and yes: many developers contributed to both projects.
Nevertheless, <span class="caps">GIMP</span> remains fairly independent, and while it consumes the
<span class="caps">GNOME</span> platform, it tends to do so in its own way, and under its own direction.</p>
<p>There’s another issue to be considered, when it comes to “<span class="caps">GNOME</span> applications”,
especially at the very beginning of the project: <span class="caps">GNOME</span> was not, and is not,
a monolithic entity. There’s no such thing as “<span class="caps">GNOME</span> developers”, unless you
mean “people writing code under the <span class="caps">GNOME</span> umbrella”. Anyone can come along,
write an application, and call it “a <span class="caps">GNOME</span> application”, assuming you used a
copyleft license, <span class="caps">GTK</span> for the user interface, and the few other <span class="caps">GNOME</span> platform
libraries for integrating with things like settings. At the time, code hosting
and issue trackers weren’t really a commodity like nowadays—even SourceForge,
which is usually thought to have always been available, would become public in
1999, two years after <span class="caps">GNOME</span> started. <span class="caps">GNOME</span> providing <span class="caps">CVS</span> for hosting your code,
infrastructure to upload and mirror release archives, and a bug tracker, was
a large value proposition for application developers that were already
philosophically and technologically aligned with the project. Additionally,
if you wanted to write an application there was a strong chance that you had
contributed, or you were at least willing to contribute, to the platform
itself, given its relative infancy. As we’ve seen in episode 1.4, having
commit access to the source code repository meant also having access to all
the <span class="caps">GNOME</span> modules; the intent was clear: if you’re writing code good enough
for your application that it ought to be shared across the platform, you
should drive its inclusion in the platform.</p>
<p>As we’ve seen all the way back in episode 1.1, <span class="caps">GNOME</span> started off with a few
“core” applications, typically utilities for the common use of a workstation
desktop. In the 1.0 release, we had the <span class="caps">GNOME</span> user interface around Miguel de
Icaza’s Midnight Commander file manager; the Electric Eyes image viewer,
courtesy of Carsten Haitzler; a set of small utilities, in the “gnome-utils”
grab bag; and <em>three</em> text editors: GXedit, gedit, and gnotepad+. I guess this
decision to ship all of them was made in case a <span class="caps">GNOME</span> user ended up on a desert
island, and once saved by a passing ship after 10 years, they could be able to
say: “this is the text editor I use daily, this is the text editor I use in the
holidays, and that’s the text editor I will <strong>never</strong> use”.</p>
<p>Alongside this veritable text editing bonanza, we could also find a small <span class="caps">PIM</span>
suite, with GnomeCal, a calendar application, and GnomeCard, a contacts
applications; and a spreadsheet, called Gnumeric.</p>
<p>The calendar application was written by Federico Mena in 1998, on a dare from
Miguel, in about ten days, and it attempted to replicate the offerings of
commercial Unix operating systems, like Solaris. The contacts application was
written by Arturo Espinosa pretty much at the same time. GnomeCal and
GnomeCard could read and export the standard vCal and vCard formats,
respectively, and that allowed integration with existing software on other
platforms, as an attempt to “lure away” users from those platforms and
towards <span class="caps">GNOME</span>.</p>
<p>Gnumeric was the brain child of Miguel, and the first real attempt at
pushing the software platform forward; the original <span class="caps">GNOME</span> canvas
implementation, based on the Tk canvas, was modified not only to improve the
performance, but also to allow writing custom canvas elements in order to
have things like graphs and charts. The design of Gnumeric was mostly mutuated
from Excel, but right from the start the idea was to ensure that the end
result would surpass Excel and its limitations—which was a somewhat tall
order for an application developed by volunteers; it clearly demonstrates
the will to not just copy commercial products, but to improve on them, and
deliver a better experience to users. Gnumeric, additionally, came with a
plugin infrastructure that exposed the whole workbook, sheet, and cells to
each plugin.</p>
<p>Both the <span class="caps">PIM</span> applications and the spreadsheet application integrated with
the object model effort, and provided components to let other applications
embed or manipulate their contents and data.</p>
<p>While Gnumeric is still active today, 20 years and three major versions later,
both GnomeCal and GnomeCard were subsumed into what would become one of the
centerpieces of Ximian: Evolution.</p>
<p><span class="caps">GNOME</span> 1.2 remained pretty much similar to the 1.0, from an application
perspective. Various text editors were moved out of the release, and went
along at their own pace, with gedit being the main survivor; Electric Eyes
fell into disrepair, and was replaced by the Eye of <span class="caps">GNOME</span> as an image
viewer. The newly introduced ggv, a <span class="caps">GTK</span>-based <span class="caps">GUI</span> layer around ghostscript,
was the postscript document viewer. Finally, for application developers,
a tool called “Glade” was introduced as a companion to every programmer’s
favourite text editor. Glade allowed creating a user interface using
drag and drop from a palette of components; once you were done with it,
it would generate the C code for you—alongside the needed Autotools gunk
to build it, if it couldn’t find any. The generated code was limited to
specific files, so as long as you didn’t have the unfortunate idea of hand
editing your user interface, you could change it through Glade, generate
the code, and then hook your own application logic into it.</p>
<p>Many projects of that era started off with generated code, and if you’re
especially lucky, you will never have to deal with it, unless, of course,
you’re trying to write something like the history of the <span class="caps">GNOME</span> project.</p>
<p>For <span class="caps">GNOME</span> 1.4 we only see Nautilus as the big change in the release, in
terms of applications. Even with an effort to ensure that applications,
as well as libraries, exposed components for other applications to reuse,
most of the development effort was spent into laying down the groundwork
of the desktop itself; applications came and went, but were leaf nodes in
the graph of dependencies, and as such required less coordination in their
development, and fewer formalities when it came to releasing them to the users.</p>
<p>It would be a long time before somebody actually sat down, and decided
what kind of applications ought to be part of the <span class="caps">GNOME</span> release.</p>
<hr class="docutils" />
<p>With this episode, we’ve now reached the end of the first chapter of
the history of the <span class="caps">GNOME</span> project. The second chapter, as I said a few
weeks ago, will be on January 17th, as for the next four weeks I’m going
to be busy with the end of the year holidays here in London.</p>
<p>Once we’re back, we’re going to have a little bit of a retrospective on
the first chapter of the history of <span class="caps">GNOME</span>, before plunging directly into
the efforts to release <span class="caps">GNOME</span> 2.0, and what those entailed.</p>
<p>So, see you next year for Chapter 2 of the History of <span class="caps">GNOME</span>.</p>
<div class="section" id="references">
<h2>References</h2>
<ul class="simple">
<li><a class="reference external" href="https://www.bassi.io/category/history/podcast.xml">Subscribe</a> to this podcast</li>
<li>Music: “<a class="reference external" href="http://freemusicarchive.org/music/Lee_Rosevere/Music_For_Podcasts/Lee_Rosevere_-_Music_For_Podcasts_-_04_Looking_Back">Looking Back</a>“, by Lee Rosevere - <span class="caps">CC</span> <a class="reference external" href="https://creativecommons.org/licenses/by/4.0/">by-4.0</a></li>
<li>Logo: The History of <span class="caps">GNOME</span>, by <a class="reference external" href="http://jimmac.musichall.cz/">Jakub Steiner</a></li>
<li>This podcast is released under a <a class="reference external" href="https://creativecommons.org/licenses/by-sa/4.0/">Creative Commons license</a></li>
<li><a class="reference external" href="https://www.bassi.io/articles/2018/10/25/table-of-contents/">Table of contents</a>.</li>
</ul>
</div>
Episode 1.b: Bindings2018-12-06T16:00:00+00:002019-02-24T19:18:04+00:00ebassitag:www.bassi.io,2018-12-06:/articles/2018/12/06/history-of-gnome-episode-1-b/<p class="first last">A side episode! Language bindings in the early <span class="caps">GNOME</span> era take the front stage</p>
<p>Back in episode 1.1 and 1.2 we talked a bit about “language bindings” as a
resource available to <span class="caps">GNOME</span> application developers that did not want to deal
with C as the programming language for their projects.</p>
<p>Language bindings for <span class="caps">GTK</span> appeared pretty much as soon as the <span class="caps">GIMP</span> adopted
it, in order to write plugins for custom image processing effects; adding new
filters just by dropping a file in a well-known location made <span class="caps">GIMP</span> extensible
without requiring recompiling the whole application. So it’s not weird that
the original announcement for the <span class="caps">GNOME</span> project mentions applications and
desktop components written in Guile: since the very beginning, the intent was
to only use C for core libraries of the <span class="caps">GNOME</span> platform, in order to have an
application development stack consumable through programming languages other
than C.</p>
<p>Of course, best laid plans, and all that.</p>
<p>If we look at the history of the bindings in <span class="caps">GNOME</span> we can divide it into two
major eras: before introspection, and after introspection. We’re going to
concentrate on the former, and leave the latter when we move on to the second
chapter of the main narrative.</p>
<p>The first few bindings for <span class="caps">GTK</span> appearing on the scene were Objective C, C++,
and Guile. The first two were definitely easier to achieve, as both Objective
C and C++ are compatible with the C standard of the time; it was easy to build
shallow abstractions over the C <span class="caps">API</span>, and if you needed something more
complicated, you could always drop into C. Guile, on the other hand, used a
<span class="caps">LISP</span> grammar; and even though it managed to call into C exactly the same, you
could not really expose a pointer to a C data structure and tell developers to
go to town on it, so you had to be careful in both what you exposed, and what
you abstracted away.</p>
<p>After Objective C, C++, and Guile, Perl and Python bindings started to appear,
as the market share for both those languages increased.</p>
<p>The shared problem among all of them was that <span class="caps">GTK</span> was growing as a toolkit,
and its <span class="caps">API</span> surface started to exceed the capacity for a human being to hand
code the various entry points by reading a C header file and translating it
into the intended code for each programming language to trampoline into the
underlying library.</p>
<p>It is a well established fact that programmers, left to their own devices,
will try to program their way out of every problem. In this particular case,
each binding started growing its own set of tools to generate code from C
headers; then the script were modified to read C headers and ancillary
metadata, needed to handle cases where C was sadly not enough to describe
the <span class="caps">API</span> and the relation between widgets, like inheritance; or the
idiosynchrasies of the <span class="caps">GTK</span> code base, like constructor arguments (the
precursor to GObject’s properties) and signals.</p>
<p>The C++ bindings grew a veritable cornucopia of exceedingly 1997 decisions, like:</p>
<blockquote>
<ul class="simple">
<li>a set of hand coded header files that included parsing directives
and metadata, alongside real C++ code</li>
<li>a set of m4 macros to do text processing over the generated files
to replace some of the directives</li>
<li>a lexer and a parser, generated via flex and bison, to generate C++
code out of the generated headers</li>
</ul>
</blockquote>
<p>I looked at the early C++ bindings and I’m still in awe of the Rube Goldberg
machine that was used to build them; the only things missing in the project
build were a small marble, an hamster wheel, two pool cues, and a bowling bowl.</p>
<p>Every binding, though, had their own way of generating code, and thus their
own way of storing metadata to describe the <span class="caps">GTK</span> <span class="caps">API</span>. To avoid the
proliferation of ad hoc formats, and to provide a relatively official
description of the <span class="caps">API</span>, <span class="caps">GTK</span> developers decided to settle to a common
definition file, mutuated from the Guile bindings.</p>
<p>The definition file used an S-expression syntax, and it described:</p>
<blockquote>
<ul class="simple">
<li>enumeration types, which contained not only the C symbol, but also
a “nick name”, that is a string that could be used to map it to the
numeric value</li>
<li>boxed types, that is plain old data structures</li>
<li>object types, that is classes in the type system</li>
<li>functions, each with its return type and arguments</li>
</ul>
</blockquote>
<p>The definition files were originally a mix of hand written changes on top
of the output generated by a script that would parse the C header files;
this meant that they would go out of sync pretty quickly. Additionally,
they were lacking a lot of ancillary information because the script did not
know anything about the <span class="caps">GTK</span> type system. Various language bindings took the
definition files and copied them into their own source repositories, to
tweak them to fit their code generation steps; this introduced an additional
drift into the already cumbersome process of keeping language bindings up to
date with a fast moving library like <span class="caps">GTK</span>.</p>
<p>An attempt at improving and standardising the definition file format came
in the late 1999 and early 2000, courtesy of Elliot Lee and Havoc
Pennington. The s-expression syntax was maintained, but the data set was
extended to allow matching objects with namespaces; methods with classes;
and parameters with their types, names, and default values.</p>
<p>Of course, this still required generating C code out of a formally agnostic
S-expression, generated from C code; this meant that bindings for dynamic
languages were all but dynamic, and that additions to the <span class="caps">GTK</span> <span class="caps">API</span> would
still require an additional step in order to be consumed by anything that
wasn’t C, or C adjacent. For a while, though, this was good enough for
application developers, and the ability to quickly write small tools or
large applications, in Python, or Perl, or <span class="caps">PHP</span>, or Ruby, made the <span class="caps">GNOME</span>
platform quite attractive.</p>
<p>Still, many applications in the <span class="caps">GNOME</span> project were written in C because
that’s what C developers will do when given then chance — and that’s what
we’re going to see in next week’s side episode.</p>
<div class="section" id="references">
<h2>References</h2>
<ul class="simple">
<li><a class="reference external" href="https://www.bassi.io/category/history/podcast.xml">Subscribe</a> to this podcast</li>
<li>Music: “<a class="reference external" href="http://freemusicarchive.org/music/Lee_Rosevere/Music_For_Podcasts/Lee_Rosevere_-_Music_For_Podcasts_-_04_Looking_Back">Looking Back</a>“, by Lee Rosevere - <span class="caps">CC</span> <a class="reference external" href="https://creativecommons.org/licenses/by/4.0/">by-4.0</a></li>
<li>Logo: The History of <span class="caps">GNOME</span>, by <a class="reference external" href="http://jimmac.musichall.cz/">Jakub Steiner</a></li>
<li>This podcast is released under a <a class="reference external" href="https://creativecommons.org/licenses/by-sa/4.0/">Creative Commons license</a></li>
<li><a class="reference external" href="https://www.bassi.io/articles/2018/10/25/table-of-contents/">Table of contents</a>.</li>
</ul>
<ul class="simple">
<li><a class="reference external" href="https://mail.gnome.org/archives/gtk-list/1997-June/msg00145.html">C++ wrapper - Gtk—</a></li>
<li><a class="reference external" href="https://mail.gnome.org/archives/gtk-list/1997-August/msg00164.html">Perl Gtk module uploaded to <span class="caps">CPAN</span></a></li>
<li><a class="reference external" href="https://mail.gnome.org/archives/gtk-list/1997-November/msg00180.html"><span class="caps">ANNOUNCE</span>: Python-Gtk Version 0.4</a></li>
<li><a class="reference external" href="https://mail.gnome.org/archives/gtk-list/1997-November/msg00195.html">GUBIs gtk.def</a></li>
<li><a class="reference external" href="https://mail.gnome.org/archives/gtk-devel-list/2000-January/msg00070.html">defs files</a></li>
</ul>
</div>
Episode 1.a: The GIMP Toolkit2018-11-29T16:00:00+00:002019-03-14T15:06:51+00:00ebassitag:www.bassi.io,2018-11-29:/articles/2018/11/29/history-of-gnome-episode-1-a/<p class="first last">A side episode! We’re going to take a look to <span class="caps">GTK</span> in its first major <span class="caps">API</span> cycle</p>
<p>The history of the <span class="caps">GNOME</span> project is also the history of its core development
platform. After all, you can’t have a whole graphical environment without a way
to write not only applications for it, but its own components. Linux, for better
or worse, does not come with its own default <span class="caps">GUI</span> toolkit; and if we’re using
<span class="caps">GNU</span> as the userspace environment we’re still out of something that can do the
job of putting things on the screen for people to use.</p>
<p>While we can’t tell the history of <span class="caps">GNOME</span> without <span class="caps">GTK</span>, we also cannot tell the
history of <span class="caps">GTK</span> without <span class="caps">GNOME</span>; the two projects are fundamental entwined, both
in terms of origins and in terms of direction. Sure, <span class="caps">GTK</span> can be used in
different environments, and on different platforms, now; and having a
free-as-in-free software <span class="caps">GUI</span> toolkit was definitely the intent of the separation
from <span class="caps">GIMP</span>’s own code base; nevertheless, <span class="caps">GTK</span> as we know it today would not
exist without <span class="caps">GNOME</span>, just like <span class="caps">GNOME</span> would not have been possible without <span class="caps">GTK</span>.</p>
<p>We’ve talked about <span class="caps">GTK</span>’s origin as the replacement toolkit for Motif created by
the <span class="caps">GIMP</span> developers, but we haven’t spent much time on it during the first
chapter of the history of the <span class="caps">GNOME</span> project.</p>
<p>If you managed to fall into a time hole, and ended up in 1996 trying to write
a <span class="caps">GUI</span> application on Linux or any commercial Unix, you’d have different choices
depending on:</p>
<blockquote>
<ul class="simple">
<li>the programming language you wish to use in order to write your application</li>
<li>the license you wish to use once you release your application</li>
</ul>
</blockquote>
<p>The academic and professional space on Unix was dominated by OpenGroup’s Motif
toolkit, which was mostly a collection of widgets and utilities on top of the
X11 toolkit, or Xt. Xt’s <span class="caps">API</span>, like all pre-Xorg X11 libraries <span class="caps">API</span>, is very 1987:
pointers hidden inside type names; global locks; explicit event loop. Xt is also
notable because it lacks basically any feature outside of “initialise an
application singleton”; “create this top level window”, either managed by a
window manager compatible with the Inter-Client Communication Conventions
Manual, or an unmanaged “pop up” window that is left to the application
developer to handle; and an event dispatch <span class="caps">API</span>, in order to write your own
event loop. Motif integrated with Xt to provide everything else: from buttons
to text entries; from menus to scroll bars.</p>
<p>Motif was released under a proprietary license, which required paying royalties
to the Open Group.</p>
<p>If you wanted to release your application under a copyleft license, you’d
probably end up writing something using another widget collection, released
under the same terms as X11 itself, and like Motif based on the X toolkit,
called the “X Athena widgets”—which is something I would not wish on my worst
enemy; you could also use the GNUstep project toolkit, a reimplementation of
the NeXT frameworks; but like on NeXT, you’d have to use the (then) relatively
niche Objective C language.</p>
<p>If you didn’t want to suffer pain and misery with the Athena widgets, or you
wanted to use anything except Objective C, you’d have to write your own layout,
rendering, and input handling layer on top of raw Xlib calls — an effort
commonly known amongst developers as: “writing a <span class="caps">GUI</span> toolkit”.</p>
<p><span class="caps">GIMP</span> developers opted for the latter.</p>
<p>Since <span class="caps">GTK</span> was a replacement for an extant toolkit, some of the decisions that
shaped its <span class="caps">API</span> were clearly made with Motif terminology in mind: managed
windows (“top levels”), and unmanaged windows (“pop ups”); buttons and toggle
buttons; menus and menu shells; scale and spin widgets; panes and frames.
Originally, <span class="caps">GTK</span> objects were represented by opaque integer identifiers, like
objects in OpenGL; that was, fortunately, quickly abandoned in favour of
pointers to structures. The widget hierarchy was flat, and you could not derive
new widgets from the existing ones.</p>
<p><span class="caps">GTK</span> as a project provided, and was divided into three basic and separate libraries:</p>
<blockquote>
<ul class="simple">
<li>GLib, a C utility library, meant to provide the fundamental data
structures that the C standard library does not have: linked lists,
dynamic arrays, hash tables, trees</li>
<li><span class="caps">GDK</span>, or the <span class="caps">GIMP</span> drawing toolkit, which wrapped Xlib function calls and
data types, like graphic contexts, visuals, colormaps, and display connections</li>
<li><span class="caps">GTK</span>, or the <span class="caps">GIMP</span> toolkit, which contained the various <span class="caps">UI</span> elements, from
windows, to buttons, to labels, to menus</li>
</ul>
</blockquote>
<p>Once <span class="caps">GTK</span> was spun off into its own project in late 1996, <span class="caps">GTK</span> gained the
necessary new features needed to write applications and libraries. The
widget-specific callback mechanism to handle events was generalised into
“signals”, which are just a fancy name for the ability to call a list of
functions tied to a well known name, like “clicked” for buttons, or
“key-press-event” for key presses. Additionally, and more importantly, <span class="caps">GTK</span>
introduced a run time type system that allowed deriving new widgets from
existing ones, as well as creating new widgets outside of the <span class="caps">GTK</span> source
tree, to allow applications to define their own specialised <span class="caps">UI</span> elements.</p>
<p>The new <span class="caps">GTK</span> gained a “plus”, to distinguish it from the in-tree <span class="caps">GTK</span> of the
early <span class="caps">GIMP</span>.</p>
<p>Between 1996 and 1997, <span class="caps">GTK</span> development was mostly driven by the needs of <span class="caps">GIMP</span>,
which is why you could find specialised widgets such as a ruler or a color
wheel in the standard <span class="caps">API</span>, whereas things like lists and trees of widgets were
less developed, and amounted to simple containers that would not scale to large
sets of items—as any user of the file selection widget at the time would be
able to tell you.</p>
<p>Aside from the <span class="caps">API</span> being clearly inspired by Motif, the appearance of <span class="caps">GTK</span> in
its 0.x and 1.0 days was very much Motif-like; blocky components, 1px shadows,
aliased arrows and text. Underneath it all, X11 resources like windows,
visuals, server-side allocated color maps, graphic contexts, and rendering
primitives. Yes, back in the old days, <span class="caps">GTK</span> was fully network transparent, like
every other X11 toolkit. It would take a few more years, and two major <span class="caps">API</span>
cycles, for <span class="caps">GTK</span> to fully move away from that model, and towards its own custom,
client-side rendering.</p>
<p><span class="caps">GTK</span>’s central tenets about how the widget hierarchy worked were also mutuated
in part from Motif; you had a tree of widgets, starting from the top level
window, down to the leaf widgets like text labels. Widgets capable of holding
other widgets, or containers, were mostly meant to be used as layout managers,
that is <span class="caps">UI</span> elements encoding a layout policy for their children widgets—like
a table, or an horizontal box. The layout policies provided by <span class="caps">GTK</span> eschewed the
more traditional “pixel perfect” positioning and sizing, as provided by the
Windows toolkits; or the “struts and springs” model, provided by Apple’s
frameworks. With <span class="caps">GTK</span>, you packed your widgets inside different containers, and
those would be sized by their contents, as well as by packing options, such as
a child filling all the available space provided by its parent; or allowing the
parent widget to expand, and aligning itself within the available space.</p>
<p>As we’ve seen in episode 1.2, one of the first things the Red Hat Advanced
Development labs did was to get <span class="caps">GTK</span> 1.0 out of the door in advance of the
<span class="caps">GNOME</span> 1.0 release, in order to ensure that the <span class="caps">GNOME</span> platform could be consumed
both by desktop components and by applications alike. While that happened, <span class="caps">GTK</span>
started its 1.2 development cycle.</p>
<p>The 1.2 development effort went into stabilisation, performance, and the
occasional new widget. GLib was split off from <span class="caps">GTK</span>’s repository at the start of
the development cycle, as it was useful for other, non-<span class="caps">GUI</span> C projects.</p>
<p>As a result of “Project Bob”, Owen Taylor took the code initially written by
Carsten Haitzler, and wrote a theming engine for <span class="caps">GTK</span>. <span class="caps">GTK</span> 1.0 gave you <span class="caps">API</span>
inside <span class="caps">GDK</span> to draw the components of any widget: lines, polygons, ovals, text,
and shadows; the <span class="caps">GDK</span> <span class="caps">API</span> would then call into the Xlib one, and send commands
over the wire to the X server. In <span class="caps">GTK</span> 1.2, instead of using the <span class="caps">API</span> inside <span class="caps">GDK</span>,
you’d go through a separate layer inside <span class="caps">GTK</span>; you had access to the same
primitives, augmented by a state tracker for things like colors, background
pixmaps, and fonts. That state was defined via an ancillary configuration file,
called a “resource” file, with its own custom syntax. You could define the
different colors for each different state for each widget class, and <span class="caps">GTK</span> would
store that information inside the style state tracker, ready to be used when rendering.</p>
<p>Additionally, <span class="caps">GTK</span> allowed to load “engines” at run time: small pieces of
compiled C code that would be injected into each <span class="caps">GTK</span> application, and that
replaced the default drawing implementation provided by <span class="caps">GTK</span>. Engines would have
access to the style information, and, critically, as we’re going to see in a
moment, to the windowing system surface that the widget would be drawn into.</p>
<p>It’s important to note that theme engines in <span class="caps">GTK</span> 1.2 could only influence
colors and fonts; additionally, widgets would be built in isolation one from
the other, from disjoint primitives. As a theme engine author you had no idea
if the text you’re drawing is going to end up in a button, or inside a label;
or if the background you’re rendering is going to be a menu or a top level
window. Which is why theme engine authors tried to be sneaky about this; due
to how <span class="caps">GTK</span> and <span class="caps">GDK</span> were split, the <span class="caps">GDK</span> windowing system surface needed to have
an opaque back pointer to the <span class="caps">GTK</span> widget that owned it. If you knew this detail,
you could obtain the <span class="caps">GTK</span> widget currently being drawn, and determine not only
the type of the widget, but also its current position within the scene graph.
Of course, this meant that theme engines, typically developed in isolation,
would need to be cognisant of the custom widgets inside applications, and that
any bug inside an engine would either break new applications, which had no
responsibility towards maintaining an internal state for theme engines to poke
around with, or simply crash everything that loaded it. As we’re going to see
in the future, this approach to theming—at the same time limited in what it
could achieve out of the box, and terrifyingly brittle as soon as it was fully
exploited—would be a problem that both <span class="caps">GTK</span> developers and <span class="caps">GNOME</span> theme
developers would try to address; and to the surprise of absolutely nobody, the
solution made a bunch of people deeply unhappy.</p>
<p>Given that <span class="caps">GTK</span> 1.2 had to maintain <span class="caps">API</span> and <span class="caps">ABI</span> compatibility with <span class="caps">GTK</span> 1.0,
nothing much changed over the course of its development history. By the time
<span class="caps">GNOME</span> 1.2 was released, the idea was to potentially release <span class="caps">GTK</span> 1.4 as an
interim version. A new image loading library, called GdkPixbuf, was written
to replace the aging imlib2, using the <span class="caps">GTK</span> type system. Additionally, as we
saw in episode 1.5, Owen Taylor was putting the finishing touches on a text
shaping library called Pango, capable of generating complex text layouts
through Unicode strings. Finally, Tim Janik was hard at work on a new type
system, to be added to GLib, capable of being used for more than just <span class="caps">GUI</span>
development. All of these changes would require a clear cut with the backward
compatibility of the 1.x series, which meant that the 1.3 development cycle
would result in <span class="caps">GTK</span> 2.0, and thus would be part of the <span class="caps">GNOME</span> 2.0 development
effort—but this is all in the future. Or in the past, if you think fourth dimensionally.</p>
<p>Next week, we’re going to see what happens when people that really don’t want
to use C to write their applications need to deal with the fact that the whole
toolkit and core platform they are consuming is written in object oriented C,
in the side episode about language bindings.</p>
<div class="section" id="references">
<h2>References</h2>
<ul class="simple">
<li><a class="reference external" href="https://www.bassi.io/category/history/podcast.xml">Subscribe</a> to this podcast</li>
<li>Music: “<a class="reference external" href="http://freemusicarchive.org/music/Lee_Rosevere/Music_For_Podcasts/Lee_Rosevere_-_Music_For_Podcasts_-_04_Looking_Back">Looking Back</a>“, by Lee Rosevere - <span class="caps">CC</span> <a class="reference external" href="https://creativecommons.org/licenses/by/4.0/">by-4.0</a></li>
<li>Logo: The History of <span class="caps">GNOME</span>, by <a class="reference external" href="http://jimmac.musichall.cz/">Jakub Steiner</a></li>
<li>This podcast is released under a <a class="reference external" href="https://creativecommons.org/licenses/by-sa/4.0/">Creative Commons license</a></li>
<li><a class="reference external" href="https://www.bassi.io/articles/2018/10/25/table-of-contents/">Table of contents</a>.</li>
<li><a class="reference external" href="http://www.opengroup.org/openmotif/docs/m213.pdf">Motif 2.1 Programmer’s Guide</a></li>
<li><a class="reference external" href="https://web.archive.org/web/20110530191612/http://developer.gnome.org:80/gtk-faq/stable/x90.html">What is the + in <span class="caps">GTK</span>+? (cached)</a></li>
</ul>
</div>
Episode 1.5: End of the road2018-11-22T14:00:00+00:002019-02-24T19:17:49+00:00ebassitag:www.bassi.io,2018-11-22:/articles/2018/11/22/history-of-gnome-episode-1-5/<p class="first last"><span class="caps">GNOME</span> 1.4 gets released, and development work for <span class="caps">GNOME</span> 2 being in earnest; but things come crashing down once the Dot com bubble bursts, and <span class="caps">GNOME</span> loses an ally</p>
<p>With version 1.2 released, and the Foundation, well, founded, the <span class="caps">GNOME</span>
community was free to spend its time on planning the next milestones for the
project, namely: the last release of the 1.x cycle, and the first of the 2.x one.</p>
<p><span class="caps">GNOME</span> 1.4 was supposed to be a technology preview for some of the changes in
the platform that were going to be the centerpiece of the <span class="caps">GNOME</span> 2.0 core
platform; additionally, applications such as Nautilus, Evolution, and Gnumeric
were going to be added to the release as official <span class="caps">GNOME</span> components. Maciej
Stachowiak volunteered to work as the release manager, and herd all the module
maintainers in order to put all the cats in a row for long enough to get
distribution-ready tarballs out of them.</p>
<p>From the technological side, one of the long term integration jobs was finally
coming to fruition: a specification for interoperation between <span class="caps">GNOME</span> and the
various window managers available on X11. The work, which was started back in
the pre-1.0 beta days by Carsten Haitzler with the goal to make Enlightenment
work with the <span class="caps">GNOME</span> components, attempted to specify various properties and
protocols to be implemented by window managers and toolkits, in order to allow
those toolkits to negotiate capabilities and perform operations that involved
the window manager, such as making windows full screen, for media players and
presentation tools; listing the number of virtual workspaces available, and
moving windows across them; defining “struts”, or areas of the screen occupied
by special parts of the desktop, such as panels, and that would not be covered
when resizing or maximising windows; specifying properties for icons, window
titles, and other ancillary information to be displayed by session components
such as windows and task lists; creating protocols for embedding windows from
different processes, used to implement panel applets, and shared components.</p>
<p>The Extended Window Manager Hints specification, built on top of the original
Inter-Client Communication Convention Manual, or <span class="caps">ICCCM</span>, was the first attempt
at real cross-desktop collaboration, with developers for toolkits, window
managers, and desktop environments working together in order to let applications
behave somewhat consistently, regardless of the environment in which they were
being used. Alongside the <span class="caps">EWMH</span>, additional specifications like the <span class="caps">XEMBED</span>
protocol, the clipboard specification, and the <span class="caps">XSETTINGS</span> specification, were
created as foundations to the concept of a “free desktop” stack, and we’ll see
where that will lead in the <span class="caps">GNOME</span> 2 days.</p>
<p>The creation of the <span class="caps">EWMH</span> gave a further push towards the establishment of a
default window manager for <span class="caps">GNOME</span>. Enlightenment had steadily fallen out of
favour, given its increase in complexity and its own push towards becoming a
full environment in its own right. Additionally, its release planning had
become somewhat erratic, with Linux distributions often forced to package
development snapshots of dubious quality. While Enlightenment was never
considered the default window manager for the <span class="caps">GNOME</span> desktop environment, it
was the most integrated for a long while, especially compared to the older,
or more “free spirited” ones, meant mostly to be used by people building their
own environments out of tinfoil, toothpicks, hopes, and prayers. Nevertheless,
it became clear that having at least a sensible default, released alongside
the rest of the project’s modules and maintained within the same community,
and more importantly with the same goals, was necessary. The choice for a
window manager fell on Sawmill, a window manager written in a Lisp-like
language called “rep”, and originally released around the same time as <span class="caps">GNOME</span>
1.2. Sawmill, later renamed Sawfish to avoid a naming collision with a log
analysis software for Linux, was, like many Lisp-based projects, programmable
by the user; it was possible to write rules to match windows created by
specific applications, and apply policies, such as automatic maximisation, or
moving to specific workspaces at launch, or saving the geometry and state of
the windows when closing them. The window manager decorations were also
themable, because of course they were.</p>
<p>Another change introduced for <span class="caps">GNOME</span> 1.4 as a technology preview in preparation
for <span class="caps">GNOME</span> 2.0 was GConf, a configuration storage and notification subsystem
written by Havoc Pennington. For the 0.x snapshots and 1.0 releases, <span class="caps">GNOME</span>
provided a simple key/value configuration system called gnome-config. Each
application would use the gnome-config <span class="caps">API</span> to store and retrieve their settings,
which were saved inside flat, <span class="caps">INI</span>-like files in an hidden location under the
user’s home directory. The system was fairly simple, but not very reliable, as
it could theoretically leave files in an inconsistent state; it wasn’t at all
performant, as it needed to traverse hierarchies on the file system to get to
a key/value pair; there was no database schema for the keys and values, and it
made it impossible to validate the contents of the configuration. Additionally,
storing complex values required a fair amount of manual serialisation and
deserialisation, so application developers typically ended up writing their own
code for storing settings in flat configuration files under their own directory.
It was in theory possible to use gnome-config to store and read settings for the
desktop itself, but without a schema to define the contents of the settings and
without any type of access arbitration there was no guarantee that different
system components would end up stomping on each other’s toe, and there was no
way for interested components or applications to be notified of changes to the
configuration. The limitations of the <span class="caps">API</span> led to limitations in the user
interface: all settings and preferences were by necessity applied explicitely,
to allow saving them to a file, and ensure a consistent state of the underlying storage.</p>
<p>The goal of GConf was to provide a consistent storage for preferences. Values
would be addressed via a path, like a file system; read and write operations
would go through a session daemon, which was the onl ccomponent responsible for
reading and writing the data to disk, and would be able to subscribe listeners
to changes to a specific path. This way, applications could listen to system
configuration changes and update themselves in response to them; not only that,
but utilities could read and write well-known configuration keys, decentralising
the configuration of the environment and application from the system settings
control panel. Notification and daemon-mediated writes also made it possible to
write instant apply preferences dialogs, getting rid of the “Apply” button.</p>
<p>All seemed to be going well for the 1.4 relase, and for the 2.0 planning.</p>
<p>In January 2001, Helix Code renamed itself to Ximian, once it couldn’t secure a
trademark on the former name; nothing outside of the name of the company really changed.</p>
<p>In March 2001 Eazel released, at long last, Nautilus 1.0, and simultaneously
dropped a bomb on the community: the company was going to lay off most of its
employees, after failing to raise enough capital to keep the operations going.
Eazel finally closed down in June 2001, after only two years of operations. The
Dot-com bubble that propelled the <span class="caps">IT</span> industry into unsustainable heights was
finally bursting, and everything was crashing down, with the startups that did
not manage to get acquired by bigger companies closing left and right, leaving
engineers scrambling to get a new job in a bearish market, and leaving cheap
hardware, as well as cheap Herman Miller chairs, in the rubbish pile behind
their vacant offices.</p>
<p>Eazel published all their remaining code, and its employees promised to remain
involved in the maintenance of their projects while the community picked up
what they could; which, in fairness, they did, until most of the engineering
team moved to Apple’s web browser team.</p>
<p>Nautilus did languish after <span class="caps">GNOME</span> 1.4, but as a centerpiece for <span class="caps">GNOME</span> 2.0,
and with the port to <span class="caps">GTK</span> 2.0, it got progressively whittled down to remove the
more startuppy bits, while remaining a file manager with first class remote
volume browsing; the <span class="caps">GNOME</span> community was left with a lot of the technical debt
that remained unpaid for the following 15 years, as a result of Eazel’s abrupt end.</p>
<p>The second edition of <span class="caps">GUADEC</span>, held in Copenhagen in April 2001, right in the
middle of the Eazel drama, nevertheless featured more attendees and more
presentations, including one that would prove fundamental in setting the future
direction of the project. Sun’s usability engineer Calum Benson presented the
results of the first round of user testing on <span class="caps">GNOME</span> 1.4, and while the results
were encouraging in some areas, they laid bare the limits of the current design
approach of a mish-mash of components. If <span class="caps">GNOME</span> wanted to be usable by
professionals, not curating the offering of the desktop was not a sustainable
option any more.</p>
<p>Sun had shipped <span class="caps">GNOME</span> 1.4 as a technology preview for Solaris 8, and conducted
the first extensive set of user tests on it by sitting professional users in
front of <span class="caps">GNOME</span> and asking them to perform certain tasks, while recording their
actions. The consistency, or lack thereof, of the environment was one of the
issues that the testing immediately identified: identical functionality was
labelled differently depending on the component; settings like the fonts to be
used by the desktop were split across various places; duplication of
functionality, mostly for the sake of duplication, was rampant. Case in point:
the <span class="caps">GNOME</span> panel shipped with not one, not two, but five different clock
applets—including, of course, the binary clock, because nothing says “please
quickly tell me if it’s time for a very important meeting with my boss” like
having to read the answer in binary, from fake leds on panel 48 pixels tall.
Additionally, all those clocks, like all the panel applets, could be added and
removed by sheer accident, clicking around on the desktop. The panel itself
could simply go away, and there was no way to revert to a working state without
nuking the settings for the user. The speed of the few animated components,
like automatically hiding the panel to increase the available on screen space,
could be controlled down to the millisecond, just in case you cared, even if
the refresh rate of your display could never have the same resolution. Nautilus
had enough settings that they had to be split into a class-based system, with
Novice, Intermediate, and Expert levels, as if managing your files required
finishing <span class="caps">RPG</span> campaigns, and acquiring enough experience points from your
dungeon master.</p>
<p>The most important consequence of the usability study, though, was that in order
for <span class="caps">GNOME</span> to succeed as a desktop environment for everyone, it had to stop being
made just for the people that wrote it, or that could spend hours of time
tweaking the options, or learning how the environment worked first. Not everyone
using <span class="caps">GNOME</span> would be a white male geek in their 20s, writing free software. Web
designers, hardware engineers, financial analysists, musicians, artists,
writers, scientists, school teachers; <span class="caps">GNOME</span> 2.0 would need a set of guidelines
for applications and for the desktop, to ensure a consistent experience for
every user, similar to how the platforms provided by Apple and Microsoft defined
a common experience for the operating system.</p>
<p>In 2001, though, the Linux desktop encountered an unexpected problem, in the
same way a sentence encounters a full stop. Apple, fresh out of both the return
of its co-founder Steve Jobs, the subsequent successful product launches
for new lines of personal computers, and after releasing what was nominally a
beta in late 2000, unveiled their new operating system, <span class="caps">OS</span> X. Sure, it was
slow, buggy, and definitely nothing more than an alpha release; but it was a
shiny, nice looking desktop environment working on top of a Unix-like user
space—precisely what the free software equivalents were trying to achieve. The
free desktop community was so worried about Microsoft that it never really
considered Apple to be a contender.</p>
<p>September 2001 saw a new point release of <span class="caps">OS</span> X, and in just six months from
its initial unveiling, Apple had managed to fix most of the instability of the
previous snapshot; on top of that, they announced that all new Apple computers
starting from January 2002 would be sold with <span class="caps">OS</span> X as the default operating
system. <span class="caps">GNOME</span> 2.0 was released in June 2002, and in August 2002 Apple released
the second point release of <span class="caps">OS</span> X, which improved performance, hardware support,
and added accelerated compositing to the stack. Apple rose back from the ashes
of the ‘90s with a new, compelling software platform, and Linux found itself a
new, major competitor outside of Windows. A competitor hungry for the same kind
of professional and amateur markets that Linux was trying to target; and,
unlike Linux, Apple had both a hardware platform and a single driving force,
with enough resources to sustain this effort.</p>
<p>The fight for the survival of <span class="caps">GNOME</span>, and of the Linux desktop, was back on, and
<span class="caps">GNOME</span> had to start from 2.0.</p>
<hr class="docutils" />
<p>We have now reached the point in the story where the <span class="caps">GNOME</span> community of
volunteers and the ecosystem of companies around them is working on a new major
release of the core platform, the desktop, and the applications around it, so
it’s a good place to take a break. Writing the History of <span class="caps">GNOME</span> is quite
intensive: there’s a lot of material that needs to be researched in order to
write the 2500 words of an episode, edit them, record them, and edit the recording.</p>
<p>For the next three weeks we’re going to have some short side episodes on
technologies and applications relevant to <span class="caps">GNOME</span>; the first side episode is
going to be about <span class="caps">GTK</span>; the second will be about how <span class="caps">GNOME</span> wrote language
bindings in the early years; and the third episode will be about the first
<span class="caps">GNOME</span> applications that entered the project.</p>
<p>After these side episodes, we’re going to pause for four weeks, while I gather
my notes for the next chapter in our history of the <span class="caps">GNOME</span> project; we are going
to come back on January 17th, with the story of how <span class="caps">GNOME</span> got its groove back
with version 2.0, in the next chapter: “Perfection, improved”.</p>
<div class="section" id="references">
<h2>References</h2>
<ul class="simple">
<li><a class="reference external" href="https://www.bassi.io/category/history/podcast.xml">Subscribe</a> to this podcast</li>
<li>Music: “<a class="reference external" href="http://freemusicarchive.org/music/Lee_Rosevere/Music_For_Podcasts/Lee_Rosevere_-_Music_For_Podcasts_-_04_Looking_Back">Looking Back</a>“, by Lee Rosevere - <span class="caps">CC</span> <a class="reference external" href="https://creativecommons.org/licenses/by/4.0/">by-4.0</a></li>
<li>Logo: The History of <span class="caps">GNOME</span>, by <a class="reference external" href="http://jimmac.musichall.cz/">Jakub Steiner</a></li>
<li>This podcast is released under a <a class="reference external" href="https://creativecommons.org/licenses/by-sa/4.0/">Creative Commons license</a></li>
<li><a class="reference external" href="https://www.bassi.io/articles/2018/10/25/table-of-contents/">Table of contents</a>.</li>
<li><a class="reference external" href="https://www.linuxtoday.com/developer/2001011100204NWGN">Helix renamed to Ximian</a></li>
<li><a class="reference external" href="https://mail.gnome.org/archives/gnome-hackers/2001-May/msg00204.html">Eazel handing over modules</a></li>
<li><a class="reference external" href="https://web.archive.org/web/20010420175747/http://guadec.gnome.dk:80/"><span class="caps">GUADEC</span> 2001 (cached)</a></li>
<li><a class="reference external" href="https://lwn.net/2001/0614/a/usability-calum.php3"><span class="caps">GNOME</span> Usability</a></li>
<li><a class="reference external" href="https://en.wikipedia.org/wiki/Mac_OS_X_Public_Beta">MacOS X Public Beta</a></li>
</ul>
</div>
Episode 1.4: Founding the Foundation2018-11-15T16:00:00+00:002019-02-24T19:17:43+00:00ebassitag:www.bassi.io,2018-11-15:/articles/2018/11/15/history-of-gnome-episode-1-4/<p class="first last">Exciting times for the <span class="caps">GNOME</span> project. The first <span class="caps">GUADEC</span> is held in Paris! The Foundation gets founded! Plus: <span class="caps">CVS</span> and Bugzilla!</p>
<p>As is the case of many free and open source software projects, <span class="caps">GNOME</span>’s planning
and discussions mostly happen online, on a variety of mediums, like <span class="caps">IRC</span>, mailing
lists, and issue trackers. Some of those mediums fade out over time, replaced by
other, newer ones; or simply because they become less efficient.</p>
<p>The first 10 to 15 years of <span class="caps">GNOME</span> can be clearly traced and reconstructed from
mailing list archives, which is a very good thing, otherwise this podcast would
not be possible without spending hundreds of hours in compiling an oral history
of the project, with the chance of getting things wrong—or, at least, wronger
than I do—or simply omitting details that have long since been forgotten by the
people involved.</p>
<p>Nevertheless, it’s clear that some of the planning and discussion cannot really
happen over a written medium; the bandwidth is simply not there. Shared,
physical presence is much more efficient for human beings, in order to quickly
communicate or iterate over an idea; the details can be summarised later, once
everyone is on the same page.</p>
<p>By the year 2000, the <span class="caps">GNOME</span> project was already more than 2 years old, had its
first major release, and it was now encompassing various companies, as well as
volunteers around the globe.</p>
<p>While some of those people may have ended up sharing an office while working
for the same company, and of course everyone was on <span class="caps">IRC</span> pretty much 24/7, the
major stakeholders and contributors had yet to meet in one place.</p>
<p>Through some aggressive fundraising, what was supposed to be a small conference
organised by Mathieu Lacage in March for the benefit of the students of the
Ecole National Supérieure des Télécommunications in Paris was scaled up to
allow the attendance of 40 <span class="caps">GNOME</span> developers from around the world for four days
of presentations and discussions.</p>
<p>The main theme of the meeting was fairly ambitious: laying down the foundation
for the next major release of <span class="caps">GNOME</span>, starting from the core platform libraries,
with a new major version of <span class="caps">GTK</span>; the introduction of a comprehensive text
rendering <span class="caps">API</span> called “Pango”; and a more pervasive use of Bonobo components in
the stack.</p>
<p>Additionally, it allowed companies like Eazel, Ximian, and Red Hat, to present
their work to the larger community.</p>
<p>Owen Taylor and Tim Janik presented their plans for <span class="caps">GTK</span> 1.4, including a new
type system and improved integration with language bindings; Owen also presented
Pango, a library for text rendering in non-Latin localisations, with Unicode
support, and support for bidirectional and complex text. <span class="caps">GTK</span> was also going to
be available on Windows and BeOS, thanks to the efforts of Tor Lillqvist and
Shawn Amundson, respectively. Havoc Pennington was working on a new text editing
widget, based on Tk’s multi-line text entry. Language bindings, for C++, Python,
and Ada, were also presented, as well as applications targeting the <span class="caps">GNOME</span> platform.</p>
<p>Out of the four days of presentations, planning, discussions, and hacking came
two major results:</p>
<blockquote>
<ul class="simple">
<li>the creation of a “steering committee”, with the goal of planning and
directing the development efforts for <span class="caps">GNOME</span> 2.0</li>
<li>the push for the creation of a legal entity capable of collecting donations
on behalf of the <span class="caps">GNOME</span> project, and act as a point of reference between the
community and the commercial entities that wanted to contribute to <span class="caps">GNOME</span></li>
</ul>
</blockquote>
<p>As a side note, Telsa Gwynne’s report of <span class="caps">GUADEC</span>’s first edition is also the
first time I’ve seen an explicit mention of the “Old Farts Club”, as well as
the rule of being over 30 in order to enter it; I think we can add that to the
list of major achievements of the conference.</p>
<p>After <span class="caps">GUADEC</span>, in May 2000, <span class="caps">GNOME</span> 1.2 was released, as part of the stabilisation
of the 1.x platform, and <span class="caps">GNOME</span> 1.4 was planned by the steering committee to be
released in 2001, with the 2.0 development happening in parallel.</p>
<p>The process of creating the <span class="caps">GNOME</span> Foundation would take a few additional months
of discussions, and in July 2000 the foundation mailing list was created for
various stakeholders to outline their positions. The initial shape of the
Foundation was modelled on the Apache Software Foundation, as both a forum for
the technical direction of the project, and a place for corporations to get
involved with the project itself. The goals for this new entity, as summarised
by Bart Decrem, an Eazel co-founder, were:</p>
<blockquote>
<ol class="arabic simple">
<li>Providing a forum to determine the overall technical direction of <span class="caps">GNOME</span></li>
<li>Promoting <span class="caps">GNOME</span></li>
<li>Foster collaboration and communication among <span class="caps">GNOME</span> developers</li>
<li>Manage funds donated to the <span class="caps">GNOME</span> project</li>
</ol>
</blockquote>
<p>There was a strong objection on having corporations being able to dictate the
direction of the project, so one of the stated non-goals was for the Foundation
to not be an industry consortium, similar to The Open Group. The Foundation
would also not hire developers directly.</p>
<p>In order to avoid corporate dominance, no company would be allowed to be a
member of the foundation: if a company wanted to have a voice in the direction
of the project they could hire a Foundation member, and thus have a
representative in the community. Additionally, there would be a limit on
directors on the board working for the same company.</p>
<p>As the Foundation was going to be incorporated in the <span class="caps">US</span>, one way to avoid
both under-representation of non-<span class="caps">US</span> members and the potential of fragmentation
through separate entities in each large geographical region, was to be more
open about both the membership and the board election process. <span class="caps">GNOME</span>
contributors would be able to join the Foundation as long as they were actively
involved with the project, and each member would be eligible to be elected as
a director. Companies would be part of an advisory organ, not directly involved
in shaping the project.</p>
<p>The idea of having the Foundation be the structure for setting the technical
direction of the project was dropped fairly quickly, replaced by its function
to be the place for settling controversial decisions, leaving the maintainers
of each module in charge of their project.</p>
<p>It is interesting to note that many of the discussions that were part of the
Foundation’s initial push have yet to be given an answer, more than 15 years
later. If the Foundation is meant to be a forum for module maintainers, how do
we define which modules should be part of <span class="caps">GNOME</span>, and which ones shouldn’t? Is
being hosted on <span class="caps">GNOME</span> infrastructure enough to establish membership of a
module? And, if so, who gets to decide that a module should be hosted on <span class="caps">GNOME</span>
infrastructure? Is <span class="caps">GNOME</span> the desktop, or is that just a project under the <span class="caps">GNOME</span>
umbrella? Are applications part of <span class="caps">GNOME</span>? The <span class="caps">GNOME</span> project is, to this day,
still re-evaluating those questions.</p>
<p>Alongside the push from Eazel, Red Hat, and Ximian to get the Foundation going,
came the announcement that Sun was going to support <span class="caps">GNOME</span> as the desktop for
their Solaris operating system, in order to replace the aging <span class="caps">CDE</span>. To that goal,
Sun was going to share the resources of its engineering, design, and <span class="caps">QA</span> teams
with the <span class="caps">GNOME</span> project. Additionally, <span class="caps">IBM</span>, <span class="caps">HP</span>, and Dell wanted to support <span class="caps">GNOME</span>
through the newly created Foundation.</p>
<p>Surprisingly, the discussions over the Foundation proceeded quickly; the
self-imposed deadline for the announcement was set for August 15, 2000, three
years after the first announcement of the <span class="caps">GNOME</span> project, to presented at the
Linux World Expo, a trade fair with a fair amount of media exposure. The
creation of the actual legal entity, an initial set of bylaws, and the election
of a board of directors would follow.</p>
<p>Having a few of the hot startups in the Linux space, as well as well
established companies in the <span class="caps">IT</span> sector, come together and announce they were
putting their weight behind the <span class="caps">GNOME</span> project would, of course, be spun in a
way that was adversarial to Microsoft, and so it was. The press release at
the <span class="caps">LWE</span> pushed the angle of a bunch of companies joining together to challenge
Microsoft, using a bunch of free code wrote by hacker weirdos to do so.</p>
<p>The announcement of the <span class="caps">GNOME</span> Foundation did not impress the <span class="caps">KDE</span> project, which
released a statement trying to both downplay the importance of <span class="caps">GNOME</span> and of the
companies that pledged resources to the <span class="caps">GNOME</span> project.</p>
<p>In November 2000, after finalising the initial set of bylaws for the Foundation
and opening the membership to the people contributing to the project, <span class="caps">GNOME</span> held
the first ever elections for the position of director of the board. With 33
candidates, a pool of 370 possible voters, and 330 valid ballots in the box,
the first eleven directors were:</p>
<blockquote>
<ul class="simple">
<li>Miguel de Icaza (Helix Code)</li>
<li>Havoc Pennington (Red Hat)</li>
<li>Owen Taylor (Red Hat)</li>
<li>Jim Gettys (Compaq)</li>
<li>Federico Mena Quintero (Helix Code)</li>
<li>Bart Decrem (Eazel)</li>
<li>Daniel Veillard (<span class="caps">W3C</span>)</li>
<li>Dan Mueth (Eazel)</li>
<li>Maciej Stachowiak (Eazel)</li>
<li>John Heard (Sun Microsystems)</li>
<li>Raph Levien (Eazel)</li>
</ul>
</blockquote>
<p>Additionally, the advisory board was thus composed:</p>
<blockquote>
<ul class="simple">
<li>Compaq</li>
<li>Eazel</li>
<li>Free Software Foundation</li>
<li>Gnumatic</li>
<li>Helix Code</li>
<li>Henzai</li>
<li><span class="caps">IBM</span></li>
<li>Object Management Group</li>
<li>Red Hat</li>
<li>Sun Microsystems</li>
<li><span class="caps">VA</span> Linux</li>
</ul>
</blockquote>
<p>After the election, the new board started working in earnest on the process for
incorporating the foundation and registering it as a non-profit entity; this
took until March 2001, after a couple of false starts. In the meantime, the main
topics of discussions were:</p>
<blockquote>
<ul class="simple">
<li>the foundation bylaws, needed for the incorporation, the tax-exempt status,
and for opening a bank account in order to receive membership fees from the
advisory board</li>
<li>the <span class="caps">GNOME</span> 1.4 release management, handled by Maciej Stachowiak</li>
<li>the preparation for the 2nd edition of <span class="caps">GUADEC</span>, to be held in Denmark</li>
</ul>
</blockquote>
<p>Additionally, the <span class="caps">GNOME</span> Foundation was going to work on establishing a trademark
for the project, both as the name <span class="caps">GNOME</span> and for the project’s logo.</p>
<p>Originally, the <span class="caps">GNOME</span> logo was not a logo at all. It was part of a repeating
pattern for one of the desktop backgrounds, designed by Tuomas Kuosmanen, who
also designed Wilber, the <span class="caps">GIMP</span> mascot. Tuomas reused the foot pattern as an
icon for the panel, namely the button for the launcher menu, which contained
a list of common applications, and let the user add their own.</p>
<p>In a typical free software spirit, and with a certain amount of bravery
considering the typical results for such requests, Red Hat decided to host a
competition for the logo setting as the prize for the winning submission a
graphic tablet; they also asked contestants to use <span class="caps">GIMP</span> to create the logo,
which, sadly, precluded the ability to get vector versions of it. In the end,
many good submissions notwithstanding, the decision fell to a modified version
of the original foot, also done by Tuomas—only instead of a right foot, it was
a left foot, shaped like a “G”.</p>
<p>Leaving aside the administrivia of the Foundation for a moment, let’s go back
to the technical side of the <span class="caps">GNOME</span> project, and take small detour to discuss
the tools used by <span class="caps">GNOME</span> developers. Over the years these tools have changed, in
many cases for the better, but it helps to understand why these changes were
made in the first place, especially for newcomers that did not experience how
life was way back when developers had to bang rocks together to store the code,
use leaves and twigs to compile it, and send pigeons to file bug reports.</p>
<p><span class="caps">GNOME</span> code repositories started off using <span class="caps">CVS</span>. If you know, even in passing,
what Git is, you can immediately think of <span class="caps">CVS</span> as anything that Git isn’t.</p>
<p><span class="caps">CVS</span> was slow; complicated; obscure; unforgiving; not extensively documented;
with a terrible user experience; and would fail in ways that could leave both
the local copy of the code and the remote one in a sorry state for everyone.</p>
<p>No, hold on.</p>
<p>Sorry, that’s precisely like Git.</p>
<p>Well, except “slow”.</p>
<p>Unlike Git, though, all the operations on the source revisions were done on
the server, which meant that you didn’t have access to the history of the
project unless you were online, and that you couldn’t commit intermediate
states of your work without sending them to the server. Branching was terrible,
so it was only done when strictly necessary. These limitations influenced many
of the engineering practices of the time; you had huge change log files in
place of commit logs; releases were only marked as such by virtue of having
generated an archive, as tagging was atrocious; the project history was stored
per-file, so you would not have the ability to see a change in its entirety
unless you manually extracted a patch between two revisions; conflicts between
developers working on the same tree were a daily occurance, and made integration
of different work a pain.</p>
<p>It was not odd to have messy history in the revision control, as well as having
to ask the <span class="caps">CVS</span> administrators to roll back a change to a previously backed up
version, to compensate for some bad commit or source tree surgery.</p>
<p>Due to how <span class="caps">GNOME</span> components were initially developed — high level modules with
shared functionality which were then split up — having commit access to one
module’s repository allowed access to every other repository. This allowed
people to work on multiple modules, and encouraged contributions across the
whole code base, especially from newcomers. As a downside, it would lead to
unreviewed commits and flames on mailing lists.</p>
<p>All in all, though, the “open doors” policy for the repositories worked well
enough, and has been maintained over the years, across different source
revision control software, and has led to not only many “drive by” patches,
but also to a fair number of bugs being fixed.</p>
<p>Talking about bugs, the end of 2000 was also the point when the <span class="caps">GNOME</span> project
moved to Bugzilla as their bug tracking system.</p>
<p>Between the establishment of the project and October 2000, <span class="caps">GNOME</span> used the same
software platform also used by Debian to track bugs in the various modules.
The Debian Bug Tracking System was, and still is, email based. You’d write an
email, fill out a couple of fields with the module name and version, add a
description of the issue and the steps to reproduce it, and then send it to
<a class="reference external" href="mailto:submit@bugs.gnome.org">submit@bugs.gnome.org</a>. The email would be sent to the owner of the module, who
would then be able to reply via email, add more people to the email list, and
in general control the status of the bug report through commands sent, you
guessed, by email. The web interface at bugs.gnome.org would show the email
thread, and let other people read and subscribe to it by sending an email, if
they were experiencing the same issue, or were simply interested in it.</p>
<p>By the late 2000, the amount of traffic was enough to make the single machine
dedicated to the <span class="caps">BTS</span> keel over and die; so, a new solution was being sought,
and it presented itself in the form of Bugzilla, a bug tracking system
originally developed by Mozilla as a replacement for the original Netscape
in house bug tracker once Netscape published their code in the open.</p>
<p>The web-friendly user interface; the database-backed storage for bug reports;
and the query system made Bugzilla a very attractive proposition for the
<span class="caps">GNOME</span> project. Additionally, Eazel and Ximian were already using Bugzilla for
their own projects, which made the choice much more easy to make. Bugzilla went
on to be the bug tracking system for <span class="caps">GNOME</span> for the following 18 years.</p>
<p>By the end of the millenium, <span class="caps">GNOME</span> was in a good position, with a thriving
community of developers, translators, and documentation writers; taking
advantage of the licensing woes of the “Kompetition”, and with a major release
under its belt, the project now had commercial backing and a legal entity
capable of representing, and protecting, the community. The user base was
growing on Linux, and with Sun’s committment to move to <span class="caps">GNOME</span> for their next
version of Solaris, <span class="caps">GNOME</span> was one step away from becoming a familiar
environment for millions of users.</p>
<p>This is usually when the ground falls beneath your feet.</p>
<hr class="docutils" />
<p>While <span class="caps">GNOME</span> developers, community members, and companies were off gallivanting
in the magical world of foundations and transformative projects, the rest of
the <span class="caps">IT</span> world was about to pay its dues. The bubble that had been propelling
the unsustainable amount of growth of the previous 3 years was about to burst spectacularly.</p>
<p>We’re going to see the effects of the end of the Dot com bubble on the <span class="caps">GNOME</span>
project in next week’s episode, “End of the road”.</p>
<div class="section" id="references">
<h2>References</h2>
<ul class="simple">
<li><a class="reference external" href="https://www.bassi.io/category/history/podcast.xml">Subscribe</a> to this podcast</li>
<li>Music: “<a class="reference external" href="http://freemusicarchive.org/music/Lee_Rosevere/Music_For_Podcasts/Lee_Rosevere_-_Music_For_Podcasts_-_04_Looking_Back">Looking Back</a>“, by Lee Rosevere - <span class="caps">CC</span> <a class="reference external" href="https://creativecommons.org/licenses/by/4.0/">by-4.0</a></li>
<li>Logo: The History of <span class="caps">GNOME</span>, by <a class="reference external" href="http://jimmac.musichall.cz/">Jakub Steiner</a></li>
<li>This podcast is released under a <a class="reference external" href="https://creativecommons.org/licenses/by-sa/4.0/">Creative Commons license</a></li>
<li><a class="reference external" href="https://www.bassi.io/articles/2018/10/25/table-of-contents/">Table of contents</a>.</li>
<li><a class="reference external" href="https://en.wikipedia.org/wiki/GUADEC"><span class="caps">GUADEC</span></a></li>
<li><a class="reference external" href="https://web.archive.org/web/20000510153443/http://www.guadec.enst.fr/"><span class="caps">GUADEC</span> 2000 (cached)</a></li>
<li><a class="reference external" href="https://web.archive.org/web/20010611111353/http://www.linux.org.uk:80/Guadec.html">Telsa Gwynne report from <span class="caps">GUADEC</span> (cached)</a></li>
<li><a class="reference external" href="https://static.lwn.net/2000/0323/a/gnomesum.html"><span class="caps">GNOME</span> Summary, Feb-Mar 2000</a></li>
<li><a class="reference external" href="https://www.theregister.co.uk/2000/08/14/sun_puts_dollars_and_suits/">Sun moves Solaris to <span class="caps">GNOME</span></a></li>
<li><a class="reference external" href="https://mail.gnome.org/archives/gnome-list/2000-July/msg00367.html">Draft proposal for the <span class="caps">GNOME</span> Foundation</a></li>
<li><a class="reference external" href="https://mail.gnome.org/archives/foundation-list/2000-October/msg00250.html">Final list of candidates for the <span class="caps">GNOME</span> Foundation elections</a></li>
<li><a class="reference external" href="https://mail.gnome.org/archives/foundation-announce/2000-November/msg00002.html">Preliminary results from the <span class="caps">GNOME</span> Foundation elections</a></li>
<li><a class="reference external" href="http://web.archive.org/web/20001018074911/http://bugs.gnome.org:80/">bugs.gnome.org (cached)</a></li>
</ul>
</div>
Episode 1.3: Land of the bonobos2018-11-08T16:00:00+00:002019-02-24T19:17:35+00:00ebassitag:www.bassi.io,2018-11-08:/articles/2018/11/08/history-of-gnome-episode-1-3/<p class="first last">New companies form around <span class="caps">GNOME</span>! Ximian and Eazel work on making <span class="caps">GNOME</span> useful for corporate environments and casual users, and start shaping the technology stack with components for applications to use and reuse.</p>
<p>With the <span class="caps">GNOME</span> 1.0 release, and the initial endorsement of the project by Linux
distributors like Red Hat and Debian, it was only a matter of time before a
commercial ecosystem would start to coalesce around the <span class="caps">GNOME</span> project. The first
effort was led by Red Hat, with its Red Hat Advanced Development laboratories.
Soon, others would follow.</p>
<p>Two companies, in particular, shaped the early landscape of <span class="caps">GNOME</span>: Ximian and Eazel.</p>
<p>Ximian was announced alongside <span class="caps">GNOME</span> 1.0, at the Linux Expo 1999, by none
other than the project creator and lead, Miguel de Icaza, and his friend and
<span class="caps">GNOME</span> contributor, Nat Friedman; it was meant to be a company that would work
on <span class="caps">GNOME</span>, and provide support for it, in a model similar to the Red Hat one,
but with a more focused approach than a whole Linux distribution. Initially,
the name of the company was “International <span class="caps">GNOME</span> Support”, before being renamed
Helix Code first, and Ximian in 2001, when it proved impossible to secure a
trademark on Helix Code. For the benefit of clarity, I’ll use “Ximian”
throughout this episode, even if we’re going to cover events that transpired
while the company name was still called “Helix Code”.</p>
<p>Ximian’s initial effort was mostly spent towards raising capital to hire
developers to work on <span class="caps">GNOME</span> and its applications, using the extant community
as a ready made talent pool, like many other companies did, and still do to
this day.</p>
<p>Ximian focused on:</p>
<ul class="simple">
<li>providing <span class="caps">GNOME</span> as a commercially supported independent platform on
top of existing Unix-like operating systems</li>
<li>jump-starting an application ecosystem that would target <span class="caps">GNOME</span> and
focused on enterprise-oriented platforms</li>
</ul>
<p>The results of these two areas of focus were Red Carpet and Ximian <span class="caps">GNOME</span>, for
the former; and Evolution, for the latter.</p>
<p>Red Carpet was a way to distribute software developed at its own pace, on top
of different Linux and Unix-like operating systems. Whether you were running
Red Hat Linux, SuSE, Debian, Mandrake, or Solaris; whether your platform was
Intel-based or <span class="caps">PPC</span>-based; whether you had a fully supported <span class="caps">OS</span> or you were
simply an enthusiast and early adopter; you’d be able to download a utility
that checked the version of a known list of components on your system;
downloaded the latest version of those components and their dependencies from
the Ximian server; installed the packages that would fit with your system; and
kept them up to date every time a new version was released upstream. All of
this was managed through a user-friendly interface, definitely nicer than the
alternatives provided at the time by any other distribution, with clear
indications of software sources; out of date packages; and suggested updates.</p>
<p>Through Red Carpet, Ximian created Ximian <span class="caps">GNOME</span>, a software distribution channel
that delegated the maintenance of the desktop environment and selected
applications, to an entity outside of the one that provided the core <span class="caps">OS</span>, and
allowed to keep <span class="caps">GNOME</span> updated at the pace of upstream development, minus the
time for <span class="caps">QA</span>.</p>
<p>Of course, this whole approach relied on the desktop being fully separate from
the core <span class="caps">OS</span>, something that was possible only back in the early days of Linux
as a competitive workstation operating system; additionally, it wasn’t something
devoid of risks. Linux installations have always leaned towards heavy
customisation post-installation, with the combinatorial explosion of packages
available for every user and system administator to install on top of a base
system, and without a clear separation of namespaces between the core <span class="caps">OS</span>, the
graphical environment, and the user applications. Bolting a whole new desktop
environment on top of a fluid base system could be problematic at best, and it
made upgrading the base system, the desktop environment, and the applications
an interesting challenge—with “interesting” defined as “we’re all going to die”.
Red Carpet did allow, though, for keeping a stable base underneath a fast moving
target like <span class="caps">GNOME</span>, assuming that you only cared about upgrading <span class="caps">GNOME</span>.</p>
<p>Another advantage of Red Carpet was the ability to provide a standard upgrade
path for a fleet of systems, which is what made it attractive for system administrators.</p>
<p>Outside of Red Carpet, Ximian was working on Evolution, an email client and
groupware platform for enterprise environment.</p>
<p>Email clients for Linux, like text editors and <span class="caps">IRC</span> clients, are a dime a dozen,
but mostly terminal-based, and a poor fit in an enterprise environment, as they
would not integrate with groupware services, like calendars, events, or shared
address books. Sure, you could script your way out of most anything, if you
were playing at being a <span class="caps">BOFH</span>, but Carol in Human Resources would not be able to
get away with not receiving her email because Charlie made an error writing a
procmail rule, and sent all the notifications halfway to Siberia.</p>
<p>Groupware suites are pretty much a requirement in corporate environments, and
since contracts with those corporations can make or break a company, having a
client capable of not only providing the same features, but also integrating
with existing infrastructure is a fundamentally interesting proposition.</p>
<p>While Ximian tried to break into the commercial space through the tried and
tested route of support contracts and enterprise software, Eazel chose a
different, and somewhat risker route.</p>
<p>Eazel was a case of building a company around an idea 10 years too early; the
idea in question being: providing remote storage for files and application,
complete with browsing remote volumes and folders from your file manager as if
they were on local storage. All of this would happen on the nascent Linux
desktop, which meant creating a fair amount of the necessary infrastructure and
shaping the design and user interaction at the same time.</p>
<p>Eazel was founded by many former Apple employees, including Andy Hertzfeld and
Darin Adler, who were the technical leads of the original Macintosh team; and
Susan Kare, the designer of the Macintosh icons and typefaces.</p>
<p>The main entry point for users of Eazel services was a new file manager, called
Nautilus, coupled to a virtual file system abstraction that would be designed
specifically for graphical user interfaces, to avoid blocking the <span class="caps">UI</span> while
file operations on resources with large latency, like a network volume, were
in progress. Given that fuzzy licensing situation around <span class="caps">KDE</span> and Qt, Eazel
decided to work with the <span class="caps">GNOME</span> community, and took the remarkable step of
developing Nautilus in the open.</p>
<p>Eazel started working their way upstream with the <span class="caps">GNOME</span> community around the
late 1999/early months of 2000, thanks to Maciej Stachowiak, an early hire and
a <span class="caps">GNOME</span> developer who worked on Guile, as well as various <span class="caps">GNOME</span> applications
and components. Of course, the first thing the core <span class="caps">GNOME</span> developers did when
approached by Eazel was to port their code from its early C++ incarnation to
C, to fit in with the rest of the platform. The interesting thing that happened
was that Eazel developers complied with that request, and stuck with the project.</p>
<p>At the time, <span class="caps">GNOME</span>’s file manager was a <span class="caps">GUI</span> layer around Miguel de Icaza’s
Midnight Commander, a Norton Commander clone; <span class="caps">MC</span> was mostly used as a terminal
application, even if it could integrate with different <span class="caps">GUI</span> toolkits, like Tk
and <span class="caps">GTK</span> when running under X11. The accretion of various <span class="caps">GUI</span> led to cruft
accumulating as well, and some of the design requirements for a responsive <span class="caps">UI</span>
in a desktop environment poorly fit in with how a terminal <span class="caps">UI</span> would work.
Additionally, maintenance was, as it’s wont, mostly volunteer-based. Federico
Mena spent time, while working at the <span class="caps">RHAD</span> labs, on the <span class="caps">GNOME</span> integration, and
that was the closest thing to somebody being paid to work on the <span class="caps">MC</span> code base.
As the work on Nautilus progressed both from Eazel and the <span class="caps">GNOME</span> community, the
scale slowly tipped in favour of the more integrated, desktop-oriented file
manager with paid maintenance.</p>
<p>Of course, what we call Nautilus (or “Files”) today was a very different beast
than what it was when Eazel introduced it to the <span class="caps">GNOME</span> community in
February 2000. Folder views could have annotations, you could add emblems on
files and folders, set custom icons, and even custom backgrounds. The file view
was a canvas, with the ability to zoom in and out the grid of icons, or even
stretch the icons and change their sizes, as well as their position. You could
have live preview of files directly inside Nautilus without opening a different
application, and thanks to the work done at Red Hat by Christopher Blizzard on
integrating the Mozilla web rendering engine with <span class="caps">GTK</span>, Nautilus could also
browse the web, or more likely your company’s intranet, or WebDAV shares.</p>
<p>While working on Nautilus, Eazel also provided functionality to the core <span class="caps">GNOME</span>
platform; mainly, the <span class="caps">GNOME</span> <span class="caps">VFS</span> library was made available outside of Nautilus
as a way to perform file operations outside of the simple <span class="caps">POSIX</span> <span class="caps">API</span>, and other
applications quickly started using it to access remote volumes, or to integrate
functionality like copying and moving files; libeel, a library for custom
widgets used in Nautilus; and librsvg, a library for rendering <span class="caps">SVG</span> files,
mostly graphical assets used by Nautilus for its icons, saw adoption on the
<span class="caps">GNOME</span> stack.</p>
<p>Despite the differences in the originating companies, both Nautilus and
Evolution shared a common design philosophy: componentisation. Not just
plugins and extension modules, but whole components for integrating
functionality into, and from, those applications.</p>
<p>The “object model environment” that begat the <span class="caps">GNOME</span> project’s acronym, and was
simmering in the background, started getting into full swing between <span class="caps">GNOME</span> 1.0
and 1.2, with complex applications exposing components for other applications
to reuse and re-arrange, as well as taking components themselves and adding new
functionality without necessarily adding new dependencies.</p>
<p>Instead of raw <span class="caps">CORBA</span>, Eazel and Ximian worked on <span class="caps">OAF</span>, the object activation
framework, a way to enumerate, activate, and watch components on a system;
and Bonobo, a library that made it easy to write <span class="caps">GUI</span> components for <span class="caps">GNOME</span>
applications to use, reducing the <span class="caps">CORBA</span> boilerplate.</p>
<p>The idea was that applications would mostly be “shells” that instantiated
components, either provided by themselves or by other projects, and build
their <span class="caps">UI</span> out of them. Things like menus and actions associated with those
menus could be described in <span class="caps">XML</span>, everyone’s favourite markup language, and
exposed as part of the component. <span class="caps">GNOME</span> and its applications would be a
collection of libraries and small processes, combined and recombined as the
users needed them.</p>
<p>Of course, the first real application of this design was to embed the
Minesweeper game into Gnumeric, the spreadsheet application, because why
wouldn’t you?</p>
<p>The effort didn’t stop there, though; Evolution was, in fact, a shell with
email client, calendar, and address book components. Nautilus used Bonobo
to componentise functionality outside the file management view, like the web
rendering, or the audio playback and music album view.</p>
<p>This was the heyday of the component era of <span class="caps">GNOME</span>, and while its promises of
shiny new functionality were attractive to both platform and application
developers, the end result was by and large one of untapped potential.
Componentisation requires a large initial effort in designing the architecture
of an application, and it’s really hard to introduce after the fact without
laying waste to working code. As an initial roadblock it poorly fits with the
“scratch your own itch” approach of free and open source software. Additionally
it requires not just a higher level of discipline in the component design and
engineering, it also depends on comprehensive and extensive documentation,
something that has always been the Achille’s Heel of many an open source project.</p>
<p>In practice, the componentisation started and stopped around the same time as
<span class="caps">GNOME</span> 2 was being developed, and for a long while in the history of the project
it was the proverbial dead albatross stuck around the neck of the platform.</p>
<p>For <span class="caps">GNOME</span> developers in mid-2000, though, this was a worthy goal to pursue,
so they worked hard on stabilising the platform while applications iterated
over it.</p>
<p>The project’s efforts moved in two prongs: minor releases for the 1.x platform,
and planning towards the 2.0 release</p>
<p><span class="caps">GNOME</span> 1.2 was released as a minor improvement over the existing 1.0 platform in
May 2000, and a similar 1.4 minor release was planned for mid-2001; meanwhile,
the effort of integrating the functionality spun off in libraries like Bonobo
and <span class="caps">OAF</span>, gnome-vfs and librsvg, into the core platform was well underway.</p>
<hr class="docutils" />
<p>Ximian and Eazel helped shape <span class="caps">GNOME</span>’s future not just by creating products
based on the <span class="caps">GNOME</span> desktop and platform, or by hiring <span class="caps">GNOME</span> developers; they
also contributed to establish two very important parts of the <span class="caps">GNOME</span> community,
that exist to this day: <span class="caps">GUADEC</span>, the <span class="caps">GNOME</span> conference; and the <span class="caps">GNOME</span> Foundation.</p>
<p>Next week we’re going to witness the birth of both <span class="caps">GUADEC</span> and the Foundation,
and we’ll take a small detour to look at the tools used by the <span class="caps">GNOME</span> project to
host their code and track the bugs contained in said code, in “Founding the Foundation”.</p>
<div class="section" id="references">
<h2>References</h2>
<ul class="simple">
<li><a class="reference external" href="https://www.bassi.io/category/history/podcast.xml">Subscribe</a> to this podcast</li>
<li>Music: “<a class="reference external" href="http://freemusicarchive.org/music/Lee_Rosevere/Music_For_Podcasts/Lee_Rosevere_-_Music_For_Podcasts_-_04_Looking_Back">Looking Back</a>“, by Lee Rosevere - <span class="caps">CC</span> <a class="reference external" href="https://creativecommons.org/licenses/by/4.0/">by-4.0</a></li>
<li>Logo: The History of <span class="caps">GNOME</span>, by <a class="reference external" href="http://jimmac.musichall.cz/">Jakub Steiner</a></li>
<li>This podcast is released under a <a class="reference external" href="https://creativecommons.org/licenses/by-sa/4.0/">Creative Commons license</a></li>
<li><a class="reference external" href="https://www.bassi.io/articles/2018/10/25/table-of-contents/">Table of contents</a>.</li>
<li><a class="reference external" href="https://en.wikipedia.org/wiki/Eazel">Eazel</a></li>
<li><a class="reference external" href="https://en.wikipedia.org/wiki/Ximian">Ximian</a></li>
<li><a class="reference external" href="http://web.archive.org/web/20120214200704/http://primates.ximian.com/~miguel/helix-history.html">History of Helix Code (cached)</a></li>
<li><a class="reference external" href="https://www.techrepublic.com/article/let-red-carpet-keep-your-linux-machines-up-to-date/">Red Carpet</a></li>
<li><a class="reference external" href="http://web.archive.org/web/20000815204422/http://nautilus.eazel.com:80/screenshots/aug-02-2000/">Nautilus screenshots (cached)</a></li>
</ul>
</div>
Episode 1.2: Desktop Wars2018-11-01T17:00:00+00:002019-02-24T19:17:29+00:00ebassitag:www.bassi.io,2018-11-01:/articles/2018/11/01/history-of-gnome-episode-1-2/<p class="first last">The desktop wars begin, and competitions heats up. We’re going to see the initial reaction to <span class="caps">KDE</span>’s licensing woes in the larger Linux ecosystem, and how that played into <span class="caps">GNOME</span>’s adoption. Plus: Red Hat enters the fray, and creates the Red Hat Advanced Development laboratories, and <span class="caps">GNOME</span> releases version 1.0</p>
<p>The year is 1998.</p>
<p>In an abandoned warehouse in San Francisco, in a lull between the end of the
previous rave and the beginning of the next, the volume of the electronica has
been turned all the way down; the strobes and lasers have been turned off, and
somebody cracked open one of the black tinted windows, to let some air in.
On one of the computers, made by parts scavenged here and there, with a long
since forgotten beer near its keyboard, a script is running to compile a
release archive of <span class="caps">GNOME</span> 0.20. The script barely succeeds, and the results are
uploaded to an <span class="caps">FTP</span> server, just in time for the rave to start. There’s no need
to write an announcement, the Universe will provide.</p>
<p>At the same time, somewhere in Europe, in a room dominated by large glass
windows, white walls with geometric art hanging off of them, and lots of chrome
finish, the hum of 50 developers with headphones working in concert quiets down
after the project leader, like an orchestra conductor, raises to his feet. He
looks at every young developer, from the 16 years old newcomer with a buzz
haircut, to the 25 years old grizzled old timer that will soon leave for his
5 years mandatory military service; he then looks down, and presses a key that
runs the build of a pre-release for <span class="caps">KDE</span> 1.0. The build will of course succeed
without a hitch, and the announcement will be prepared later, in the common
room, with a civilised discussion between all project members.</p>
<p>The stage is thus evenly divided.</p>
<p>The players are:</p>
<ul class="simple">
<li><span class="caps">KDE</span>, a commune of software developers using a commercially backed
toolkit to write a free software desktop environment</li>
<li><span class="caps">GNOME</span>, a rag tag band of misfits and university students, writing a
free software toolkit and desktop environment</li>
</ul>
<p>These are the desktop wars.</p>
<p>I jest, of course. The reality was wildly different than the memes. Even
calling them “the desktop wars” is really a misnomer — after all, we don’t
call the endless, tiresome arguments between Emacs and vi users as “the text
editor wars”; or the the equally exhausting diatribe between spaces and tabs
aficionados as “the code indentation wars”. At most, you could call this a
friendly competition between two volunteer projects in the same problem space,
but that doesn’t make for good, click-bait-y, tribal division.</p>
<p>Far from being a clinical, cold, and corporate-like project, <span class="caps">KDE</span> was started
by volunteers across the globe, even if it did have a strong European centre
of mass; while it did use a version of Qt not released under the terms of a
proper free software license, <span class="caps">KDE</span> had a strong ethos towards user freedom from
the very beginning, being released under the terms of the <span class="caps">GNU</span> General Public
License; and while it was heavily centralised, its code base was not a machine
of perfect harmony that never failed to build.</p>
<p><span class="caps">GNOME</span>, on the other hand, was not a Silicon Valley-by-way-of-Burning Man
product of acid casualties and runaways; its centre of mass was nearer to the
East coast of the <span class="caps">US</span> than to the West, even if <span class="caps">GIMP</span> was initially developed at
Berkeley. <span class="caps">GNOME</span> was indeed perceived as the underdog, assembled out of a bunch
of components developed at different paces, but its chaotic initial form was
both the result of the first few months of alpha releases, and of the more
conscious decision of supporting and integrating existing projects.</p>
<p>Nevertheless, the memes persisted, and the battle lines were drawn by the
larger free and open source software community pretty much immediately, like
it happened many times before, and will happen many times after.</p>
<p>The programming language was one of the factors of the division, certainly,
bringing along the extant fights between C and C++ developers, with the latter
claiming the higher technical ground by using a modern-ish language, compared
to the portable assembly of the former. <span class="caps">GNOME</span> used the existence of bindings
for other languages, like Perl, Python, Objective C, and Guile, as a way to
counter-balance the argument, by including other communities and programming
paradigms into the mix. Writing <span class="caps">GNOME</span> libraries surely was a game for C
developers, but writing <span class="caps">GNOME</span> applications was supposedly to be all about
choice. Or, at least, it would have been, once the <span class="caps">GNOME</span> libraries were done;
while the project was in its infancy, though, the same people writing the
libraries ended up writing the applications, which meant a whole lot of C being
unleashed unto the unsuspecting world.</p>
<p>From a modern perspective, relying on C as the main programming language was
probably the most contentious choice, but in the context of 1997 it would be
hard to call it surprising. Sure, C++ was already fairly well known as a system
level language, but the language itself was pretty much stuck to the 2nd
edition of “The C++ Programming Language” book, published in 1989; the first
<span class="caps">ISO</span> C++ standardisation came in 1998, followed by one 2011, 13 years later.
Additionally, programmers had been bitten by the binary incompatibilities
across compilers as well as different versions of the same compiler, while the
language evolved; and the standard library was found lacking in both design and
performance, to the point that any major C++ library, like Qt or Boost, ended
up re-implementing the same large chunks of the same basic data types. In 1997,
writing a complex, interdependent project like a desktop environment using C++
was the “edgy” effort, for lack of a better word, comparable to writing a
desktop environment in, say, Rust in 2018.</p>
<p>Another consideration, tied into the support for multiple languages, was that
basically all high level languages exposed the ability to interface their
internals using the C binary interface, as it was needed to handle the <span class="caps">OS</span>-level
integration, like file and network operations.</p>
<p>We could debate forever if C was the right choice — and there are people that
still do that to this day, so we would be in good company — but in the end the
choice was made, and it can’t be unmade.</p>
<p>By and large, though, the deciding factor that put somebody in either the <span class="caps">KDE</span>
or the <span class="caps">GNOME</span> camp was social and political; fittingly, as the free and open
source software movement is a social and political movement. The argument
boiled down to a very simple fact: the toolkit chosen by the <span class="caps">KDE</span> project was,
at the time, not distributed under a license that fit the definition of free
software, and that made redistributing <span class="caps">KDE</span> a pain for everyone that was not
the <span class="caps">KDE</span> project themselves.</p>
<p>The original Qt license, the Qt Free Edition License, required that your
own project never depended on modifications of Qt itself, and that you
licensed your own code under the terms of the <span class="caps">GPL</span>, the <span class="caps">LGPL</span>, or a <span class="caps">BSD</span>-like
license. Writing libraries depending on Qt also required to jump through
additional hoops, like sending a description of the project to Trolltech.</p>
<p>Of course, that put the <span class="caps">KDE</span> project in the clear: they were consuming Qt mostly
as a black box, and they were releasing their own code under the terms of the
<span class="caps">GPL</span>. It did place the distributors of <span class="caps">KDE</span> binaries on less certain grounds,
though, with Debian outright refusing to package <span class="caps">KDE</span> as it would put the terms
of the <span class="caps">GPL</span> used by <span class="caps">KDE</span> in direct conflict with the terms of the Qt Free Edition
License; the license itself was really not conforming to the Debian Free
Software Guidelines, so distributing Qt itself (as well as any other project
that decided to use its license) was out of the question. If you wanted to use
pre-built packages for <span class="caps">KDE</span> 1.0 on Debian, you had to download and install them
from a third party repository, maintained by <span class="caps">KDE</span> themselves.</p>
<p>Other distributions, such as SuSE and Mandrake, were less finnicky about the
details of the interaction between different licenses, and decided to ship
binary builds of <span class="caps">KDE</span> as part of their main package repositories.</p>
<p>The last big name in the Linux distributions landscape at the time was Red
Hat, and things were afoot there.</p>
<p>Just like Debian, Red Hat was less than enthused by the licensing issues of
Qt and <span class="caps">KDE</span>, and saw a fully <span class="caps">GPL</span> and <span class="caps">LGPL</span> desktop environment as a solution for
their commercial contracts. Of course, <span class="caps">GNOME</span> was mostly alpha quality software
at the time, and had to catch up pretty quickly if it wanted to be a viable
alternative to, well, shipping nothing and supporting roughly every possible
combination of window managers and utilities.</p>
<p>Which is why, in 1998, Red Hat created the Red Hat Advanced Development Laboratories.</p>
<p>The <span class="caps">RHAD</span> labs were “an independent development group to work on problems of
usability of the Linux operating system”: a few developers embedded in upstream
communities, tasked with both polishing the many, many, many rough edges of
<span class="caps">GNOME</span>, and taking over some of the unglamorous aspects of maintenance in a
large project.</p>
<p>Under the watchful eye of Mark Ewing and Michael Fulbright, <span class="caps">RHAD</span> labs hired
Elliot Lee, who wrote the <span class="caps">CORBA</span> implementation library ORBit, and worked on
the componentisation of <span class="caps">GNOME</span>; Owen Taylor, who co-maintained <span class="caps">GTK</span> and
shepherded the 1.0 release; Carsten “the rasterman” Haitzler, who wrote the
Enlightenment window manager and worked on specifying how other window
managers should integrate with <span class="caps">GNOME</span>; Jonathan Blandford, who worked on the
<span class="caps">GNOME</span> control centre; and Federico Mena, who worked on <span class="caps">GIMP</span>, <span class="caps">GTK</span>, and on the
<span class="caps">GNOME</span> <span class="caps">GUI</span> for the Midnight Commander file manager, as well as writing the
first <span class="caps">GNOME</span> calendar application. In time, the <span class="caps">RHAD</span> would acquire the talents
of other well-known <span class="caps">GNOME</span> contributors, like Havoc Pennington and Tim Janik,
to work on <span class="caps">GTK</span> 2.0; Christopher Blizzard, to work on the then newly released
Mozilla web browser; and David Mason, to work on the <span class="caps">GNOME</span> user documentation.</p>
<p>In September 1998, <span class="caps">GNOME</span> released version 0.30, to be shipped by Red Hat
Linux 5.2 as a technology preview, thanks to the work of the people at the
<span class="caps">RHAD</span> labs. Of course, it was not at all smooth sailing, and everyone there
had to fight to keep <span class="caps">GNOME</span> from getting cut — mostly by convincing the Red
Hat co-founder and <span class="caps">CEO</span>, Robert Young, that the project was in a much better
state than it looked. The now infamous “Project Bob” was a mad dash of 36 hours
for all the members of the <span class="caps">RHAD</span> labs to create and present a demo of <span class="caps">GNOME</span>,
making sure it would work — at least, within the strict confines of the demo
itself. Additionally, Carsten Haitzler would write a cool new theme using the
newly added loadable module support in <span class="caps">GTK</span>, to show off the capabilities of
the toolkit in its upcoming 1.2 release. Up until then, <span class="caps">GTK</span> looked fairly
similar to Motif, but counting on his experience on the Enlightenment window
manager, Haitzler added the ability to customise the appearance of each <span class="caps">UI</span>
element in a variety of ways.</p>
<p>Of course, in the end, it was the theme that won over Red Hat management, and
without it, “Project Bob” may have failed and spelled doom for the whole <span class="caps">RHAD</span>
labs and thus the commercial viability of the <span class="caps">GNOME</span> project.</p>
<p>In December 1998, Trolltech announced that Qt would be released under the terms
of a new license, called the “Q Public License”, or <span class="caps">QPL</span>. The <span class="caps">QPL</span> was meant to
comply with the Debian Free Software Guidelines and lead to Debian being able
to ship packages of Qt, but it did not really solve the issue of <span class="caps">GPL</span>
compatibility, so, in essence, nothing changed: Debian and Red Hat would not
ship <span class="caps">KDE</span> until its 2.0 release, 2 years later, when Trolltech relented, and
decided to ship Qt 2.0 under the terms of both the <span class="caps">QPL</span> and of the <span class="caps">GNU</span> <span class="caps">GPL</span>.</p>
<p>By the beginning of 1999, <span class="caps">KDE</span> was about to release version 1.1, whereas <span class="caps">GNOME</span>
locked down the features for a 1.0 release, which was announced in March 1999.
In April 1999, Red Hat Linux 6.0 shipped with <span class="caps">GNOME</span> as its default desktop
environment. The 1.0 release was presented at the Linux Expo, in May; the
presentation was hectic, and plagued by the typical first release issues; <span class="caps">GMC</span>,
the file manager, for instance, would crash when opening a new directory, but
the session manager was modified to restart it immediately, so all you’d see
was a window closing and re-opening in the desired location.</p>
<p>The splash of the first major release attracted attention; it was a signal that
the <span class="caps">GNOME</span> developers were ready for a higher form of war, one that involved
commercial products that could integrate with, and be based off of <span class="caps">GNOME</span>.</p>
<p>The desktop wars were entering a new phase, one where attrition and fights by
proxy would dominate the following years.</p>
<hr class="docutils" />
<p>Next week we’re going to zoom into the nascent ecosystem of companies that
were born around <span class="caps">GNOME</span>, and focus on two of them: Ximian, or Helix Code as
it was called at the time; and Eazel. Both companies defined <span class="caps">GNOME</span>’s future
in more ways than just by adoping <span class="caps">GNOME</span> as a platform; they worked within
the <span class="caps">GNOME</span> community to create products for the Linux desktop, and they shaped
the technical and social decisions of the <span class="caps">GNOME</span> project well after the first
chapter of its history.</p>
<p>Alongside that, we’re also going to look at the effort to bring about the era
of components that was initially outlined with the <span class="caps">GNOME</span> acronym: a desktop
and an object model. We’re going to see what happened to that, once we step
into “The land of the bonobos”.</p>
<div class="section" id="references">
<h2>References</h2>
<ul class="simple">
<li><a class="reference external" href="https://www.bassi.io/category/history/podcast.xml">Subscribe</a> to this podcast</li>
<li>Music: “<a class="reference external" href="http://freemusicarchive.org/music/Lee_Rosevere/Music_For_Podcasts/Lee_Rosevere_-_Music_For_Podcasts_-_04_Looking_Back">Looking Back</a>“, by Lee Rosevere - <span class="caps">CC</span> <a class="reference external" href="https://creativecommons.org/licenses/by/4.0/">by-4.0</a></li>
<li>Logo: The History of <span class="caps">GNOME</span>, by <a class="reference external" href="http://jimmac.musichall.cz/">Jakub Steiner</a></li>
<li>This podcast is released under a <a class="reference external" href="https://creativecommons.org/licenses/by-sa/4.0/">Creative Commons license</a></li>
<li><a class="reference external" href="https://www.bassi.io/articles/2018/10/25/table-of-contents/">Table of contents</a>.</li>
<li><a class="reference external" href="http://www.bassi.io/~ebassi/GNOMEvKDE.md"><span class="caps">GNOME</span> vs <span class="caps">KDE</span> (cached)</a></li>
<li><a class="reference external" href="https://www.debian.org/News/1998/19981008">Debian on <span class="caps">KDE</span> copyright and license</a></li>
<li><a class="reference external" href="http://web.archive.org/web/20000229220057/http://www.labs.redhat.com:80/index.html"><span class="caps">RHAD</span> Labs (archived)</a></li>
<li><a class="reference external" href="https://www.gnome.org/press/1999/03/gnome-1-0-released/"><span class="caps">GNOME</span> 1.0 press release</a></li>
</ul>
</div>
The History of GNOME2018-10-25T19:00:00+01:002018-12-15T14:52:38+00:00ebassitag:www.bassi.io,2018-10-25:/articles/2018/10/25/the-history-of-gnome/<p>In which I announce my podcast on the history of the <span class="caps">GNOME</span> project</p><p>I’ve done <a href="https://www.bassi.io/category/history/podcast.xml">a thing</a> which may be of interest if you’re
following the <span class="caps">GNOME</span> community.</p>
<p>As I said <a href="https://twitter.com/ebassi/status/1055492942125821952">on Twitter</a>, I have spare time, and I like boring
people to death by talking about things that matter to me a lot; one of the
things that matter to me is <span class="caps">GNOME</span> and its community—and especially its history.</p>
<p>Of course, I had to go and make it about <em>liminal spaces</em> and <em>magic
rituals</em>, because that’s what makes it fun. This, though, <strong>is</strong> a magic
ritual. I’m holding a <em>seance</em>, and I’m calling forth the past of the <span class="caps">GNOME</span>
project for the people that live down its light-cone.</p>
<p><span class="caps">GNOME</span> has the luxury of having a lot of people that stuck around—some even
from the early days when there was no <span class="caps">GNOME</span>; there are also other people,
though, some of them born after Miguel’s announcement, that are now starting
to contribute to <span class="caps">GNOME</span>. I guess that means that it’s time to look back a
bit, and give some more context to the history of the project.</p>
<p>I hope I won’t bore you that much with this; I hope that people will learn
something new, or re-discover something that was forgotten. In general, I do
hope people will have fun with it.</p>Episode 1.1: GNOME2018-10-25T16:27:00+01:002019-02-24T19:17:22+00:00ebassitag:www.bassi.io,2018-10-25:/articles/2018/10/25/history-of-gnome-episode-1/<p class="first last">The <span class="caps">GNOME</span> project is announced! We’re going to see what the Linux world looked like, and why the <span class="caps">GNOME</span> project was started; what was available at the time, for Unix and Linux users, and the beginning of complex desktop environments on Linux, ending on the first few months of the project</p>
<p>It is a long running joke in the <span class="caps">GNOME</span> community, that the acronym, or, more
accurately, the backronym, that serves as the name of the project does not
apply any more.</p>
<p>The acronym, though, has been there since the very first announcement of the
project: <span class="caps">GNOME</span>, the <span class="caps">GNU</span> Network Object Model Environment.</p>
<p>The history of <span class="caps">GNOME</span> begins on August 15th, 1997.</p>
<p><span class="caps">NASA</span> landed the Pathfinder on Mars just the month before.</p>
<p>Diana, Princess of Wales, would tragically die at the end of the month in Paris.</p>
<p>The number one song in the <span class="caps">US</span> charts is “I’ll be missing you”, by Puff Daddy.</p>
<p>On the 15th of August, Miguel de Icaza, a Mexican university student, announced
the creation of the <span class="caps">GNOME</span> project, an attempt at creating “a free and complete
set of applications and desktop tools, similar to <span class="caps">CDE</span> and <span class="caps">KDE</span> but based entirely
on free software”.</p>
<p>To understand each part of that sentence, I’m afraid we will have to go back
to a time forgotten by the laws of gods and men: the ‘80s.</p>
<p>In 1984, Richard Stallman started the <span class="caps">GNU</span> project. Don’t bother try to expand
the acronym, it’s one of those nerdy things for which the explanation is just
not as clever when said it out loud as it feels in one’s head. Incidentally,
<span class="caps">GNU</span> is the reason why the G in <span class="caps">GNOME</span> is not silent. The history of <span class="caps">GNU</span> is an
interesting topic, but we’ll avoid covering it here; if you want to, you can
read all about it on the Free Software Foundation’s website.</p>
<p><span class="caps">GNU</span> was, and it still is, an attempt at creating a Unix-compatible system that
is completely free as in “software freedom”; the freedom in question is
actually four different freedoms:</p>
<blockquote>
<ul class="simple">
<li>the freedom to use this operating system on whatever machine you want</li>
<li>the freedom to study it, down to its source code</li>
<li>the freedom to share it with others, without going through a single vendor</li>
<li>the freedom to modify it, if it does not do something you want</li>
</ul>
</blockquote>
<p>These four freedoms are enshrined in the “Copyleft” movement, which uses
distribution licenses such as the <span class="caps">GNU</span> General Public License and Lesser General
Public License, to create software programs and libraries, that respect those
four freedoms.</p>
<p><span class="caps">GNU</span> is a collection of tools, like the compiler suite and Unix-like command
line utilities, in search of a kernel capable of running them — the attempt at
creating a working, main stream <span class="caps">GNU</span> kernel is, shall we say, still in progress
to this day. In 1991, though, Linus Torvalds, a student from Finland, created
the Linux kernel, which was quickly adopted as the major platform for <span class="caps">GNU</span>, and
the rest, as they say, is history — though, like <span class="caps">GNU</span>’s history, also not the
one you’re going to hear in this podcast.</p>
<p>While the development tools and console utilities were mostly getting taken
care of, the <span class="caps">GUI</span> landscape on Linux was composed by an heterogeneous set of
tools, typically starting with a window manager; some task management tool,
like a list of running programs and a way to switch between them; and smaller
utilities, like launchers for common applications or monitoring tools for
local and network resources.</p>
<p>Each environment was typically built from the ground up and customised within
an inch of its life, a system tailored to the levels of micro-management and
pain tolerance of each Linux user, and in those days, the levels of both were
considerably high.</p>
<p>Large, integrated desktops were the remit of commercial Unix systems, like
SunOS, <span class="caps">AIX</span>, <span class="caps">HP</span>/<span class="caps">UX</span>. One of those environments was the Common Desktop Environment,
or <span class="caps">CDE</span>.</p>
<p>Created in 1993 by the Open Group, an industry consortium founded by the likes
<span class="caps">IBM</span>, Sun, and <span class="caps">HP</span>, <span class="caps">CDE</span> was built around Motif, a commercial widget toolkit
written in the late 80s by <span class="caps">HP</span> and <span class="caps">DEC</span> for the X display server, the mainstream
graphics architecture on Unix and other Unix-compatible operating systems.</p>
<p>The Open Group quickly standardised <span class="caps">CDE</span>, and until the early 2000s, when Linux
had started eating most of their lunches, it was considered the de facto
standard desktop environment for commercial Unix platforms.</p>
<p>One of the things that Linux and the commercial Unix systems running on the
Gibsons had in common was the X graphic system. This meant that you could
write applications on your Unix system at university, or at work, and then
run it on Linux at home, and vice versa.</p>
<p>As it’s often the case, Linux users wanted to bring some of the tools used on
the Big Irons to their little platform, and in 1996 a group of C++ developers
led by Matthias Ettrich, created the <span class="caps">KDE</span> project, a desktop environment in the
same vein as the <span class="caps">CDE</span> project, as the name implies. Since Motif was written in
C and released under an expensive proprietary license, they used a different
widget toolkit, written in C++, as the basis for the desktop called “Qt”
(pronounced “cute”), made by a Norwegian company called Trolltech.</p>
<p>Qt was released under a license called “The Qt Free Edition License”, which
limited the redistributability of the code: you could get the source of the
toolkit, but you could not redistribute modified versions of it. While this
was good enough if you wanted to download and build the source on your personal
computer, it put some strain on the people distributing Linux and its
software, and it went against the Copyleft ethos of the <span class="caps">GNU</span> operating system
that was the basis of Linux distributions — to the point that an effort to
reimplement a Qt-compatible widget toolkit and releasing it under a Free
Software license was started by the <span class="caps">GNU</span> project, called “Project Harmony”.</p>
<p>In the meantime, Motif was proving to be a hurdle for other free software
projects as well.</p>
<p>In 1996, Spencer Kimball and Peter Mattis, two students of the University of
California at Berkeley, wrote a raster graphics image editing tool using the
C programming language, as part of a university project, and called it the
“<span class="caps">GNU</span> image manipulation program”, or <span class="caps">GIMP</span>, as many have come to regret when
searching the name on the Internet. As university students, they had access to
Motif for free, so the initial implementation of the <span class="caps">GIMP</span> used that toolkit,
but redistribution to the world outside university, as well as technical issues
with Motif, led them to write an ad hoc <span class="caps">GUI</span> widget library for their
application, called “The <span class="caps">GNU</span> Image Manipulation Program Toolkit”, or “<span class="caps">GTK</span>” for short.</p>
<p>A community of software developers, including people that would be influential
to <span class="caps">GNOME</span> like Federico Mena and Owen Taylor, started to coalesce around <span class="caps">GIMP</span>;
they saw the value of an independent, free software widget toolkit, so <span class="caps">GTK</span> was
split off from the main <span class="caps">GIMP</span> code repository in the early 1997, and began a new
life as an independent project, released under the terms of the <span class="caps">GNU</span> Library
General Public License. The licensing terms of <span class="caps">GTK</span> allowed it to follow the
four software freedoms — use, study, share, and modify — but it also allowed
the creation of less-free software on top; as long as you distributed any
eventual changes you did to <span class="caps">GTK</span> under the same license, your application’s code
could be released under any other license you wanted. This major distinction
with Motif and Qt pushed the newer, volunteer-driven <span class="caps">GTK</span> forward, while it
filled the gaps with the older, commercially supported toolkits.</p>
<p><span class="caps">GTK</span> had a fairly lean <span class="caps">API</span>, and its use of C quickly allowed developers to write
“bindings”, that let other programmng languages use the underlying C <span class="caps">API</span> with
a minimal translation layer to pass values around. Soon after, programmers
of Perl, Python, C++, and Guile, an implementation of a dialect of the <span class="caps">LISP</span>
programming language called Scheme, could use <span class="caps">GTK</span> to write plugins for <span class="caps">GIMP</span>,
or complete, stand alone applications. Compared to <span class="caps">KDE</span>’s choice of Qt, which
only supported C++ and Python, it was a clear advantage, as it exposed <span class="caps">GTK</span>
to different ecosystems.</p>
<p>What was <span class="caps">GNOME</span> like, back in late 1997/early 1998?</p>
<p>The answer to that question is: an heterogeneous collection of tools, mostly
sharing dependencies, and developed together, that occasionally got released
and even more rarely did build out of the box without resorting to using
random snapshots out of the source revision control.</p>
<p>You had a panel, with launchers for common applications, and with a list of
running programs. There were the beginnings of a set of core applications: a
help browser; a file manager; a suite of small utilities, mostly <span class="caps">GUI</span> ports of
command line tools; games; an image viewer; a web browser based on an embeddable
<span class="caps">HTML</span> renderer; a text editor. Notably, and unlike <span class="caps">KDE</span> with its own <span class="caps">KWM</span>, not a
window manager.</p>
<p>The question of what kind of window manager should be part of a <span class="caps">GNOME</span>
environment was punted to the users — it’s actually the first instance of a
controversial thread on the <span class="caps">GNOME</span> mailing list, with multiple calls for an
“officially sanctioned” window manager, typically opposed by people happy to
let everyone use whatever they wanted — the externally developed Enlightenment
was the most common choice at the time, but you could literally run <span class="caps">GNOME</span> with
WindowMaker; GNUStep; or <span class="caps">XF</span> Window Manager 2; and you could still call it
“<span class="caps">GNOME</span>”.</p>
<p>From a code organization perspective, <span class="caps">GNOME</span> started off as a single blob, which
got progressively spun into separate components:</p>
<blockquote>
<ul class="simple">
<li>gnome-libs, a series of core libraries based off of <span class="caps">GTK</span></li>
<li>gnome-core, which contained the session manager and the panel</li>
<li>gnome-graphics, which contained the Electric Eyes image viewer</li>
<li>gnome-games, a mixed bag of simple games</li>
<li>gnome-utils, a mixed bag of <span class="caps">GUI</span> utilities, like gtop</li>
<li>gnome-admin, which contained an <span class="caps">SNMP</span> monitoring tool and Gulp, the line
printer configuration utility</li>
</ul>
</blockquote>
<p>While <span class="caps">GNOME</span> at the time was still a loosely connected set of components, the
overall direction of the design was far more grandiose, though. If you remember
the <span class="caps">GNOME</span> acronym from the announcement, it mentioned a “network object model”.</p>
<p>But what is an “object model”?</p>
<p>One of the things that Microsoft and Apple did with much fanfare, back in the
early ‘90s, was introducing the concept of embeddable components provided by
both the <span class="caps">OS</span> and applications.</p>
<p>You could create a spreadsheet, then take a section of it, embed it into your
word processor document, and edit the table from within the word processor
itself, instead of flip flopping between the two applications on the limited
screen real estate available. The idea was that documents and applications
would just be built out of blocks of data and controls, shared across the whole
operating system. Those components were available to third party developers in
programming suites like Visual Basic, Visual C++, or Delphi, and you could
quickly create a well integrated application out of them.</p>
<p>Sun tried to push Java as the foundation for this design; Apple called their
short-lived implementation of this technology “OpenDoc”; Microsoft called its
much more successful version “<span class="caps">OLE</span>”, or: Object Linking and Embedding; and, of
course, any self-respecting desktop environment competing with Windows needed
something similar to match features.</p>
<p>In order to implement an infrastructure of objects and remote procedure calls
that could be invoked on those objects you needed a communication system; <span class="caps">OLE</span>
used <span class="caps">COM</span>, the Common Object Model; <span class="caps">GNOME</span> decided to use <span class="caps">CORBA</span>, or the Common
Object Request Broker Architecture, which was not only meant to be used on local
systems but it also worked on the network. As a <span class="caps">CORBA</span> implementation, <span class="caps">GNOME</span>
started by using <span class="caps">MICO</span>, a C++ library, and then replaced it with to ORBit, a C
library written specifically for the project and addressing some of the <span class="caps">MICO</span> shortcomings.</p>
<p>While ideally <span class="caps">GNOME</span> would provide components for everything, the initial
beneficiary of this architecture was the only recognisable bit of the desktop
environment that was visible to the user all the time: the panel.</p>
<p>The contents of the panel were small, self-contained applications that would
use <span class="caps">CORBA</span> as a mechanism to negotiate being embedded into the panel own window
to display their state, or pop up things like menus and other windows on
user request.</p>
<p>The core applications started getting <span class="caps">CORBA</span>-based components, but we’ll have
to wait until after <span class="caps">GNOME</span> 1.0 to get to a widespread adoption of this architecture.</p>
<p>Between August 1997 and May 1998, <span class="caps">GNOME</span> released various 0.10 snapshots to
mark the progress of the project. By June 1998, <span class="caps">GNOME</span> 0.20 was released as a
“pseudo-beta”, followed, in September 1998, by 0.30, the first named release
of <span class="caps">GNOME</span>, called “Bouncing Bonobo”.</p>
<p>Between 0.20 and 0.30, though, something had happened: Red Hat, a Linux
distribution vendor, founded the Red Hat Advanced Development Labs, hired a
bunch of software developers that happened to contribute to <span class="caps">GNOME</span>, amongst
other things, and the benevolent corporate overlords started taking notice of
this Linux desktop.</p>
<p>Nobody really knew it at the time, but the First Desktop Wars had begun.</p>
<div class="section" id="references">
<h2>References</h2>
<ul class="simple">
<li><a class="reference external" href="https://www.bassi.io/category/history/podcast.xml">Subscribe</a> to this podcast</li>
<li>Music: “<a class="reference external" href="http://freemusicarchive.org/music/Lee_Rosevere/Music_For_Podcasts/Lee_Rosevere_-_Music_For_Podcasts_-_04_Looking_Back">Looking Back</a>“, by Lee Rosevere - <span class="caps">CC</span> <a class="reference external" href="https://creativecommons.org/licenses/by/4.0/">by-4.0</a></li>
<li>Logo: The History of <span class="caps">GNOME</span>, by <a class="reference external" href="http://jimmac.musichall.cz/">Jakub Steiner</a></li>
<li>This podcast is released under a <a class="reference external" href="https://creativecommons.org/licenses/by-sa/4.0/">Creative Commons license</a></li>
<li><a class="reference external" href="https://www.bassi.io/articles/2018/10/25/table-of-contents/">Table of contents</a>.</li>
<li>The <span class="caps">GNOME</span> <a class="reference external" href="https://mail.gnome.org/archives/gtk-list/1997-August/msg00123.html">announcement email</a></li>
<li>The <a class="reference external" href="https://www.gnu.org/gnu/manifesto.html"><span class="caps">GNU</span> Manifesto</a></li>
<li><a class="reference external" href="https://en.wikipedia.org/wiki/Common_Desktop_Environment"><span class="caps">CDE</span></a></li>
<li><a class="reference external" href="https://www.gimp.org"><span class="caps">GNU</span> Image Manipulation Program</a></li>
<li><a class="reference external" href="https://en.wikipedia.org/wiki/Object_Linking_and_Embedding"><span class="caps">OLE</span></a></li>
<li><a class="reference external" href="https://en.wikipedia.org/wiki/Common_Object_Request_Broker_Architecture"><span class="caps">CORBA</span></a></li>
<li><a class="reference external" href="https://projects-old.gnome.org/ORBit2/">Orbit</a></li>
<li><a class="reference external" href="https://mail.gnome.org/archives/gnome-list/1998-February/msg00420.html">Official <span class="caps">GNOME</span> window manager</a> thread</li>
<li><a class="reference external" href="https://mail.gnome.org/archives/gnome-announce-list/1998-June/msg00000.html"><span class="caps">GNOME</span> 0.20</a></li>
<li><a class="reference external" href="https://mail.gnome.org/archives/gnome-announce-list/1998-September/msg00005.html"><span class="caps">GNOME</span> 0.30</a></li>
</ul>
</div>
Prologue2018-10-25T16:26:00+01:002019-02-24T19:17:16+00:00ebassitag:www.bassi.io,2018-10-25:/articles/2018/10/25/history-of-gnome-episode-0/<p class="first last"><span class="caps">GNOME</span> is made by people, and people make history: complicated, recurring, funny, sad, and everything in between; this is my attempt at compiling an history of the <span class="caps">GNOME</span> project to provide a better context not just of past decisions, but also of its future directions</p>
<div class="section" id="prologue">
<h2>Prologue</h2>
<p>History is one of my passions; more than offering a narrative of the past, it
gives us context, and with context comes a better understanding of the people
that made history, and of their choices.</p>
<p>This is true for every human endeavour, and so it’s also true of software projects.</p>
<p>Free and Open Source Software is a social and political movement before a
techological one; as such, the history of a free software project should help
us contextualise not just the technological decisions, but the personalities,
the goals, and the constraintsthat influenced those decisions.</p>
<p>It’s often the case, especially in the field of software development, that
newcomers to a specific project, or a subset of a project, end up facing some
code that does not work; or that barely works, some of the time. The immediate
reaction is typically trying to understand that code, that technology, that
whole stack, in order to fix it, or replace it. Just learning what the code
does in that specific instant, though, gives only a partial picture of how
that code, that technology, that whole stack came into being.</p>
<p>It’s said that programmers think that bad code comes from bad programmers; in
reality, bad code often comes from good people operating under different
constraints than ours. Understanding those constraints, and how they changed
over time, is the first step in understanding how to write better code.</p>
<p>This is especially true of projects that involve hundreds of developers,
operating under wildly variable constraints, for decades.</p>
<p>Projects like <span class="caps">GNOME</span>.</p>
<p><span class="caps">GNOME</span> is a free software project that aims to create an environment for
computer users that is</p>
<blockquote>
<ul class="simple">
<li>easy to use</li>
<li>accessible for everyone</li>
<li>under a license that allows everyone to modify and contribute to it</li>
</ul>
</blockquote>
<p>As far as free and open source software projects go, it’s fairly old; it’s old
enough to vote, and to buy a drink in the <span class="caps">US</span>. <span class="caps">GNOME</span>, as a community, attracts
not only volunteer contributions, but commercial ones as well, and it provides
the technological underpinnings for both volunteer and commercially driven
products. It is a recognised brand, for better or worse, and it comes with
opinionated decisions, as well as an ethos that not only drives the individuals
that make it, but also the ones that use it.</p>
<p>More importantly, <span class="caps">GNOME</span> comes with a varied history, and a lot of baggage,
deeply rooted in the choices made by thousands of people. It’s something that
newcomers to the project often deal with by asking questions to whoever
appears to be knowledgeable — but it’s not something that the the project
itself offers.</p>
<p>So, I wanted to work on recontextualising <span class="caps">GNOME</span>.</p>
<p>Why me, though?</p>
<p>My history with <span class="caps">GNOME</span> does not date as far back as the project’s origins. I
did start using Linux in 1997, around the time <span class="caps">GNOME</span> was created, but for the
first couple of years I mostly used the <span class="caps">VT</span> for emails, <span class="caps">IRC</span>, Usenet, and the
occasional Napster download (kids, ask your parents); a <span class="caps">GUI</span> was something I
used to launch Netscape Navigator. I experimented with <span class="caps">KDE</span>, but I ended up
settling on WindowMaker, because I enjoyed the non-Windows-y look of the desktop.</p>
<p>As a user, I switched to <span class="caps">GNOME</span> full time by the tail end of 1.x series, and
went through the 2.0 transition with a stronger interest in the direction of
the project — to the point that I started contributing to it.</p>
<p><span class="caps">GNOME</span> is the reason I became a passionate believer not just in the freedom
of releasing the source code of your work, but also in the necessity of a
working environment for the people using a server, a workstation, a laptop,
or even a phone. I learned principles of software development; architecture;
security; privacy; hardware enablement; design and user experience; quality
assurance and testing; accessibility. I got my first job through <span class="caps">GNOME</span>, and
I met wonderful people that I consider friends, peers, and inspirations. I
lived through a huge chunk of the history of the <span class="caps">GNOME</span> project, and in some
cases I can kid myself into thinking I helped shape that history. This does
not make me the most neutral or dispassionate person to lend his perspective
on <span class="caps">GNOME</span>, but you’ll have to make do with me, I guess.</p>
<p>In the beginning this was to be a presentation, but Jonathan Blandford did an
amazing keynote on the history of <span class="caps">GNOME</span> in Manchester, at <span class="caps">GUADEC</span> 2017, for the
20th anniversary of the project, where he went through most of the milestones
and events of the previous three decades. I realised, at that point, that it
would take somebody like me, who’s less talented than Jonathan, much more than
an hour to do the same, let alone go through <span class="caps">GNOME</span>’s history in depth.</p>
<p>I started writing a series of blog posts, but halfway through I realised I was
using the same kind of voice I use when writing presentation notes, which is
when I realised I should probably just read them. Thus, a podcast.</p>
<p>So, let’s lay down some of the ground rules for this.</p>
<p>The first question is: who is this podcast for?</p>
<p>When I write, I have three broad categories of people that I want to reach; the
first is composed by people that are new, or relatively new, to the <span class="caps">GNOME</span>
project, and want to add more context to the history of the project and of the
community they just joined. In a large, established project like <span class="caps">GNOME</span>, there’s
a lot of insider information that you typically have to suss out in person, or
that you end up gleaning if you hang out in the social channels long enough.
Why is using flags in a <span class="caps">UI</span> such a problem? Why are settings expensive? What’s
up with the clocks? There is a lot of established knowledge that is only
evident by its final results, but context for those results is missing. It’s
already hard to get started in a new community, so this podcast aims at
lowering the curve for newcomers at least a bit.</p>
<p>The second audience is made by people that are not familiar with the <span class="caps">GNOME</span>
project outside of having heard about it; they are familiar enough with Linux,
but lack the “insider baseball” aspect of both the community and the technology.
They may know that <span class="caps">GNOME</span> is a desktop environment, but have no idea what a
desktop environment is made of.</p>
<p>The third audience is the <span class="caps">GNOME</span> developers themselves; people that have been
embedded in the community long enough to know some, or many, of the ins and
outs of the decisions made in the past, but they may be unfamiliar with the
whole history of the project. I hope they’ll forgive me for going on tangents,
espcially at the beginning.</p>
<p>The podcast is divided in chapters, roughly corresponding with each major
version of the desktop. There will be one episode per week, with breaks between
chapters. I’m not the fastest, nor the most adept at this medium, so I want to
allow some leeway to maintain both quality and quantity.</p>
<p>Each episode will go through events in the history of the <span class="caps">GNOME</span> project, but
I’ll try to take some time to expand on the context in the Free Software world,
as well as the rest of the happenings around the same time.</p>
<p>In some cases, I may have to give some technological definition, mostly to
ensure that we have a common vocabolary, especially for people who are not
heavily invested in the stack itself, or for people that learned English as a
second language.</p>
<p>The primary sources for this podcast are public mailing list archives;
additionally, I’ll refer to blogs from <span class="caps">GNOME</span> developers, as well as other
public sources, like interviews and presentations given at conferences. As I
said earlier, I do have direct experience of a chunk of the <span class="caps">GNOME</span> timeline, but
I’ll try my best to stick to public sources for the events that involved me as
well; this is not a “tell-all” kind of podcast. Using only public sources has
the advantage that I can refer to specific information, but has the downside
that I might miss some of the backstory; I don’t want to create an oral history
of the project, as that would be its own endeavour. I’m sure people involved in
some of the events will have their own version, and I’ll gladly accept corrections.</p>
<p>Each episode will have a companion article, which will contain the script and
the sources I’ve used.</p>
<p>Hopefully, this podcast will be interesting for both newcomers to the <span class="caps">GNOME</span>
project, and for old timers as well. Looking back to what happened helps us
shape what’s to come; and having a better understanding of the past can give
us confirmation of our choices, or a chance to revisit them.</p>
<p>Finally, I wanted to have an excuse to say out loud, with apologies to
<a class="reference external" href="http://www.revolutionspodcast.com/">Mike Duncan</a>:</p>
<p>Hello, and welcome to the history of <span class="caps">GNOME</span>.</p>
</div>
<div class="section" id="references">
<h2>References</h2>
<ul class="simple">
<li><a class="reference external" href="https://www.bassi.io/category/history/podcast.xml">Subscribe</a> to this podcast</li>
<li>Music: “<a class="reference external" href="http://freemusicarchive.org/music/Lee_Rosevere/Music_For_Podcasts/Lee_Rosevere_-_Music_For_Podcasts_-_04_Looking_Back">Looking Back</a>“, by Lee Rosevere - <span class="caps">CC</span> <a class="reference external" href="https://creativecommons.org/licenses/by/4.0/">by-4.0</a></li>
<li>Logo: The History of <span class="caps">GNOME</span>, by <a class="reference external" href="http://jimmac.musichall.cz/">Jakub Steiner</a></li>
<li>This podcast is released under a <a class="reference external" href="https://creativecommons.org/licenses/by-sa/4.0/">Creative Commons license</a>.</li>
<li><a class="reference external" href="https://www.bassi.io/articles/2018/10/25/table-of-contents/">Table of contents</a>.</li>
<li><a class="reference external" href="https://www.gnome.org"><span class="caps">GNOME</span></a></li>
</ul>
</div>
History of GNOME: Table of Contents2018-10-25T12:00:00+01:002021-02-17T18:19:41+00:00ebassitag:www.bassi.io,2018-10-25:/articles/2018/10/25/table-of-contents/<ol>
<li><a href="https://www.bassi.io/articles/2018/10/25/history-of-gnome-episode-0/">Episode 0: Prologue</a><ul>
<li>What</li>
<li>Why</li>
<li>Who</li>
<li>How</li>
</ul>
</li>
</ol>
<h2>Chapter 1: Perfection, achieved</h2>
<ol>
<li><a href="https://www.bassi.io/articles/2018/10/25/history-of-gnome-episode-1/">Episode 1.1: The <span class="caps">GNU</span> Network Object Model Environment</a><ul>
<li>Linux and Unix</li>
<li>X11, and the desktop landscape</li>
<li><span class="caps">KDE</span> and the Qt licensing</li>
<li><span class="caps">GNU</span> Image Manipulation Program</li>
<li><span class="caps">GTK</span></li>
<li>Guile and language bindings</li>
<li>Network Object Model</li>
</ul>
</li>
<li><a href="https://www.bassi.io/articles/2018/11/01/history-of-gnome-episode-1-2/">Episode 1.2: Desktop Wars …</a></li></ol><ol>
<li><a href="https://www.bassi.io/articles/2018/10/25/history-of-gnome-episode-0/">Episode 0: Prologue</a><ul>
<li>What</li>
<li>Why</li>
<li>Who</li>
<li>How</li>
</ul>
</li>
</ol>
<h2>Chapter 1: Perfection, achieved</h2>
<ol>
<li><a href="https://www.bassi.io/articles/2018/10/25/history-of-gnome-episode-1/">Episode 1.1: The <span class="caps">GNU</span> Network Object Model Environment</a><ul>
<li>Linux and Unix</li>
<li>X11, and the desktop landscape</li>
<li><span class="caps">KDE</span> and the Qt licensing</li>
<li><span class="caps">GNU</span> Image Manipulation Program</li>
<li><span class="caps">GTK</span></li>
<li>Guile and language bindings</li>
<li>Network Object Model</li>
</ul>
</li>
<li><a href="https://www.bassi.io/articles/2018/11/01/history-of-gnome-episode-1-2/">Episode 1.2: Desktop Wars</a><ul>
<li><span class="caps">KDE</span> vs <span class="caps">GNOME</span></li>
<li>C++ vs C vs Perl vs Scheme vs ObjC vs …</li>
<li>Qt vs <span class="caps">GTK</span></li>
<li>Red Hat vs the World</li>
<li>Project Bob</li>
<li><span class="caps">GTK</span> 1.2 and themes</li>
<li><span class="caps">GNOME</span> 1.0</li>
</ul>
</li>
<li><a href="https://www.bassi.io/articles/2018/11/08/history-of-gnome-episode-1-3/">Episode 1.3: Land of the bonobos</a><ul>
<li>Helix Code, and Red Carpet</li>
<li>Eazel, and Nautilus</li>
<li>Components, components, components</li>
<li><span class="caps">GNOME</span> 1.2</li>
</ul>
</li>
<li><a href="https://www.bassi.io/articles/2018/11/15/history-of-gnome-episode-1-4/">Episode 1.4: Founding the Foundation</a><ul>
<li><span class="caps">GUADEC</span></li>
<li>The <span class="caps">GNOME</span> Foundation</li>
<li>The <span class="caps">GNOME</span> logo</li>
<li>Working on <span class="caps">GNOME</span>: <span class="caps">CVS</span></li>
<li>Working on <span class="caps">GNOME</span>: Bugzilla</li>
</ul>
</li>
<li><a href="https://www.bassi.io/articles/2018/11/22/history-of-gnome-episode-1-5/">Episode 1.5: End of the road</a><ul>
<li>The window manager question</li>
<li>Sawmill/Sawfish</li>
<li>GConf</li>
<li><span class="caps">GNOME</span> 1.4</li>
<li>Sun, accessibility, and clocks</li>
<li>Dot bomb</li>
<li>Eazel’s last whisper</li>
<li>Outside-context problem: <span class="caps">OSX</span></li>
</ul>
</li>
<li><a href="https://www.bassi.io/articles/2018/11/29/history-of-gnome-episode-1-a/">Side episode 1.a: <span class="caps">GTK</span> 1</a></li>
<li><a href="https://www.bassi.io/articles/2018/12/06/history-of-gnome-episode-1-b/">Side episode 1.b: Language bindings</a></li>
<li><a href="https://www.bassi.io/articles/2018/12/14/history-of-gnome-episode-1-c/">Side episode 1.c: <span class="caps">GNOME</span> applications</a></li>
</ol>
<h2>Chapter 2: Perfection, Improved</h2>
<ol>
<li><a href="https://www.bassi.io/articles/2019/01/17/history-of-gnome-episode-2-0/">Episode 2.0: Retrospective</a></li>
<li><a href="https://www.bassi.io/articles/2019/01/24/history-of-gnome-episode-2-1/">Episode 2.1: On brand</a><ul>
<li><span class="caps">GTK</span> 2.0: <span class="caps">GTK</span> Harder</li>
<li>Fonts, icons, and Unicode</li>
<li>Configuration woes</li>
</ul>
</li>
<li><a href="https://www.bassi.io/articles/2019/02/14/history-of-gnome-episode-2-2/">Episode 2.2: Release day</a><ul>
<li>Design, usability, accessibility, and brand</li>
<li>Human Interface Guidelines</li>
<li>The cost of settings</li>
<li>Time versus features</li>
<li><span class="caps">GNOME</span> 2.0 reactions</li>
</ul>
</li>
<li><a href="https://www.bassi.io/articles/2019/02/24/history-of-gnome-episode-2-a/">Side episode 2.a: Building <span class="caps">GNOME</span></a></li>
</ol>Reference counting in modern GLib2018-09-04T16:35:00+01:002018-09-11T22:24:49+01:00ebassitag:www.bassi.io,2018-09-04:/articles/2018/09/04/ref-counting/<p>In which I explain how to implement reference counting with new GLib ≥ 2.58</p><p>Reference counting is a fairly common garbage collection technique used in
many projects; the core <span class="caps">GNOME</span> platform uses pretty much all the time, from
container data types, to GObject.</p>
<p>Implementing reference counting in C is typically fairly easy. Add a field
to your data type:</p>
<div class="highlight"><pre><span></span><code><span class="k">typedef</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">ref_count</span><span class="p">;</span>
<span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">name</span><span class="p">;</span>
<span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">address</span><span class="p">;</span>
<span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">city</span><span class="p">;</span>
<span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">age</span><span class="p">;</span>
<span class="p">}</span><span class="w"> </span><span class="n">Person</span><span class="p">;</span>
</code></pre></div>
<p>Then initialise it when creating a new instance:</p>
<div class="highlight"><pre><span></span><code><span class="n">Person</span><span class="w"> </span><span class="o">*</span>
<span class="nf">person_new</span><span class="w"> </span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="n">Person</span><span class="w"> </span><span class="o">*</span><span class="n">res</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">g_new0</span><span class="w"> </span><span class="p">(</span><span class="n">Person</span><span class="p">,</span><span class="w"> </span><span class="mi">1</span><span class="p">);</span>
<span class="w"> </span><span class="n">res</span><span class="o">-></span><span class="n">ref_count</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">res</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<p>Instead of a <code>person_copy()</code> and a <code>person_free()</code> pair of functions, you
need to write a <code>person_ref()</code> and a <code>person_unref()</code> pair:</p>
<div class="highlight"><pre><span></span><code><span class="n">Person</span><span class="w"> </span><span class="o">*</span>
<span class="nf">person_ref</span><span class="w"> </span><span class="p">(</span><span class="n">Person</span><span class="w"> </span><span class="o">*</span><span class="n">p</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="c1">// Acquire a reference</span>
<span class="w"> </span><span class="n">p</span><span class="o">-></span><span class="n">ref_count</span><span class="o">++</span><span class="p">;</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">p</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">static</span><span class="w"> </span><span class="kt">void</span>
<span class="nf">person_free</span><span class="w"> </span><span class="p">(</span><span class="n">Person</span><span class="w"> </span><span class="o">*</span><span class="n">p</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="c1">// Free the data</span>
<span class="w"> </span><span class="n">g_free</span><span class="w"> </span><span class="p">(</span><span class="n">p</span><span class="o">-></span><span class="n">name</span><span class="p">);</span>
<span class="w"> </span><span class="n">g_free</span><span class="w"> </span><span class="p">(</span><span class="n">p</span><span class="o">-></span><span class="n">address</span><span class="p">);</span>
<span class="w"> </span><span class="n">g_free</span><span class="w"> </span><span class="p">(</span><span class="n">p</span><span class="o">-></span><span class="n">city</span><span class="p">);</span>
<span class="w"> </span><span class="c1">// Free the instance</span>
<span class="w"> </span><span class="n">g_free</span><span class="w"> </span><span class="p">(</span><span class="n">p</span><span class="p">);</span>
<span class="p">}</span>
<span class="kt">void</span>
<span class="nf">person_unref</span><span class="w"> </span><span class="p">(</span><span class="n">Person</span><span class="w"> </span><span class="o">*</span><span class="n">p</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="c1">// Release a reference</span>
<span class="w"> </span><span class="n">p</span><span class="o">-></span><span class="n">ref_count</span><span class="o">--</span><span class="p">;</span>
<span class="w"> </span><span class="c1">// If this was the last reference, free the data</span>
<span class="w"> </span><span class="c1">// associated to the instance and then the instance</span>
<span class="w"> </span><span class="c1">// itself</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">p</span><span class="o">-></span><span class="n">ref_count</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">0</span><span class="p">)</span>
<span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">person_free</span><span class="w"> </span><span class="p">(</span><span class="n">p</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>Of course, <em>trivial</em> doesn’t mean <em>correct</em>. For instance, the code above
assumes that all reference acquisition and release operations will happen
from the same thread; if that’s not the case, you’ll have to use atomic
integer operations to increase and decrease the reference count.
Additionally, the code above does not check for overflows in the reference
counting, which means that the value could saturate and lead to leaks.</p>
<p>Reimplementing all the checks and behaviours is not only boring, but it
inevitably leads to mistakes along the way. For this reason, GLib 2.58
introduced two new types:</p>
<ul>
<li><code>grefcount</code>, to implement simple reference counting</li>
<li><code>gatomicrefcount</code>, to implement atomic reference counting</li>
</ul>
<p>Both come with their own <span class="caps">API</span>, which leads to this code:</p>
<div class="highlight"><pre><span></span><code><span class="k">typedef</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">grefcount</span><span class="w"> </span><span class="n">ref_count</span><span class="p">;</span>
<span class="w"> </span><span class="c1">// or: gatomicrefcount ref_count;</span>
<span class="w"> </span><span class="c1">// same as above</span>
<span class="p">}</span><span class="w"> </span><span class="n">Person</span><span class="p">;</span>
<span class="n">Person</span><span class="w"> </span><span class="o">*</span>
<span class="nf">person_new</span><span class="w"> </span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="n">Person</span><span class="w"> </span><span class="o">*</span><span class="n">res</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">g_new0</span><span class="w"> </span><span class="p">(</span><span class="n">Person</span><span class="p">,</span><span class="w"> </span><span class="mi">1</span><span class="p">);</span>
<span class="w"> </span><span class="n">g_ref_count_init</span><span class="w"> </span><span class="p">(</span><span class="o">&</span><span class="n">res</span><span class="o">-></span><span class="n">ref_count</span><span class="p">);</span>
<span class="w"> </span><span class="c1">// or: g_atomic_ref_count_init (&res->ref_count);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">res</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">Person</span><span class="w"> </span><span class="o">*</span>
<span class="nf">person_ref</span><span class="w"> </span><span class="p">(</span><span class="n">Person</span><span class="w"> </span><span class="o">*</span><span class="n">p</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="n">g_ref_count_inc</span><span class="w"> </span><span class="p">(</span><span class="o">&</span><span class="n">p</span><span class="o">-></span><span class="n">ref_count</span><span class="p">);</span>
<span class="w"> </span><span class="c1">// or: g_atomic_ref_count_inc (&p->ref_count);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">p</span><span class="p">;</span>
<span class="p">}</span>
<span class="kt">void</span>
<span class="nf">person_unref</span><span class="w"> </span><span class="p">(</span><span class="n">Person</span><span class="w"> </span><span class="o">*</span><span class="n">p</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">g_ref_count_dec</span><span class="w"> </span><span class="p">(</span><span class="o">&</span><span class="n">p</span><span class="o">-></span><span class="n">ref_count</span><span class="p">))</span>
<span class="w"> </span><span class="c1">// or: if (g_atomic_ref_count_dec (&p->ref_count))</span>
<span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">person_free</span><span class="w"> </span><span class="p">(</span><span class="n">p</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>The <code>grefcount</code> and <code>gatomicrefcount</code> make it immediately obvious that the
field is used to implement reference counting semantics; the <span class="caps">API</span> checks for
saturation of the counters, and will emit a warning; the atomic operations
are verified. Additionally, the <span class="caps">API</span> checks if you’re trying to pass an
atomic reference count to the <code>grefcount</code> <span class="caps">API</span>, and vice versa, so you have a
layer of protection there during eventual refactoring, even if the types are
both integer aliases.</p>
<p>We did not stop there, though.</p>
<p>Adding a reference count field to a structure is not always possible; for
instance, if you have <span class="caps">ABI</span> compatibility restrictions, or if the type
defition is public, adding a field may just not be something you can do
within the same <span class="caps">API</span> version. By way of an example: you may have a type meant
to be typically placed on the stack, so it needs a public, complete
declaration, in order to have a well-defined size at compile time. You may
also want to pass around an instance of the type as the argument for a
GObject signal, or as a property — but it may be expensive to copy the data
around, so you really want to have an optional reference counting mechanism
that is invisible to the vast majority of the use cases.</p>
<p>This is why we also added an allocator function that adds reference
counting semantics to the memory areas it returns, called <code>GRcBox</code>:</p>
<div class="highlight"><pre><span></span><code><span class="k">typedef</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">name</span><span class="p">;</span>
<span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">address</span><span class="p">;</span>
<span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">city</span><span class="p">;</span>
<span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">age</span><span class="p">;</span>
<span class="p">}</span><span class="w"> </span><span class="n">Person</span><span class="p">;</span>
<span class="n">Person</span><span class="w"> </span><span class="o">*</span>
<span class="nf">person_new</span><span class="w"> </span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">g_rc_box_new0</span><span class="w"> </span><span class="p">(</span><span class="n">Person</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">Person</span><span class="w"> </span><span class="o">*</span>
<span class="nf">person_ref</span><span class="w"> </span><span class="p">(</span><span class="n">Person</span><span class="w"> </span><span class="o">*</span><span class="n">p</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">g_rc_box_acquire</span><span class="w"> </span><span class="p">(</span><span class="n">p</span><span class="p">);</span>
<span class="p">}</span>
<span class="kt">void</span>
<span class="nf">person_unref</span><span class="w"> </span><span class="p">(</span><span class="n">Person</span><span class="w"> </span><span class="o">*</span><span class="n">p</span><span class="p">)</span>
<span class="p">{</span>
<span class="w"> </span><span class="c1">// person_free() is copied from the code above; we use</span>
<span class="w"> </span><span class="c1">// g_rc_box_release_full() because we have data to free</span>
<span class="w"> </span><span class="c1">// as well, but there's a variant for structures that</span>
<span class="w"> </span><span class="c1">// do not have internal allocations</span>
<span class="w"> </span><span class="n">g_rc_box_release_full</span><span class="w"> </span><span class="p">(</span><span class="n">p</span><span class="p">,</span><span class="w"> </span><span class="n">person_free</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div>
<p>As you can see, this cuts down the boilerplate and repetition considerably.</p>
<p>The data returned to you by <code>g_rc_box_new()</code> and friends is exactly the same
as you’d get from <code>g_new()</code>, so you can pass it around to your <span class="caps">API</span> exactly
the same — but it is transparently augmented with reference counting
semantics. You acquire and release references without having an explicit
reference counter. The only restriction is that you cannot reallocate the
data, so calling <code>realloc()</code> is not allowed; and, of course, you cannot free
the memory directly with <code>free()</code> — you need to release the last reference.</p>
<p>Similar to <code>GRcBox</code>, there’s a <code>GAtomicRcBox</code>, which provides atomic
reference counting semantics, with a similar <span class="caps">API</span>.</p>
<p>Both <code>GRcBox</code> and <code>GAtomicRcBox</code> are Valgrind-safe, so you won’t get false
positives or unreachable memory if run your code under Valgrind.</p>
<p>You don’t even need to have a structure to allocate: you can use <code>GRcBox</code> to
allocate any memory area with a non-zero size. Incidentally, this is how we
implemented a oft-requested feature: reference counted strings, which are
also available in GLib 2.58.</p>
<p>Reference counted strings work exactly like any other C string, but instead
of copying them and freeing them, you acquire and release references to them:</p>
<div class="highlight"><pre><span></span><code><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">s</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">g_ref_string_new</span><span class="w"> </span><span class="p">(</span><span class="s">"hello, world!"</span><span class="p">);</span>
<span class="c1">// "s" is just like any other C string</span>
<span class="n">g_print</span><span class="w"> </span><span class="p">(</span><span class="s">"s['%s'] length = %d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span><span class="w"> </span><span class="n">s</span><span class="p">,</span><span class="w"> </span><span class="n">strlen</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">));</span>
<span class="n">g_ref_string_release</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">);</span>
</code></pre></div>
<p>Reference counted strings can also be interned, which means that calling
<code>g_ref_string_new_intern()</code> with the same string twice will give you a new
reference the second time around, instead of allocating a new string; once
the last reference to the interned string is dropped, the string is
un-interned, and the resource allocated for it are freed.</p>
<p>Since you may be wary of passing around <code>char *</code> for reference counted
strings, there’s a handy <code>GRefString</code> C type you can use to improve
readability, like we have <code>GStrv</code> for <code>char **</code>:</p>
<div class="highlight"><pre><span></span><code><span class="n">GRefString</span><span class="w"> </span><span class="o">*</span><span class="n">s</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">g_ref_string_new</span><span class="w"> </span><span class="p">(</span><span class="s">"hello"</span><span class="p">);</span>
<span class="c1">// ...</span>
<span class="n">g_ref_string_release</span><span class="w"> </span><span class="p">(</span><span class="n">s</span><span class="p">);</span>
</code></pre></div>
<p><code>GRefString</code> also has an autocleanup function, so you can do:</p>
<div class="highlight"><pre><span></span><code><span class="p">{</span>
<span class="w"> </span><span class="n">g_autoptr</span><span class="p">(</span><span class="n">GRefString</span><span class="p">)</span><span class="w"> </span><span class="n">s</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">g_ref_string_new</span><span class="w"> </span><span class="p">(</span><span class="s">"hello!"</span><span class="p">);</span>
<span class="w"> </span><span class="c1">// ...</span>
<span class="p">}</span>
</code></pre></div>
<p>and the string will automatically be released once it goes out of scope.</p>pkg-config and paths2018-03-15T16:45:00+00:002018-03-15T17:00:50+00:00ebassitag:www.bassi.io,2018-03-15:/articles/2018/03/15/pkg-config-and-paths/<p>In which I explain how to use paths and pkg-config variables</p><p>This is something of a frequently asked question, as it comes up every once
in a while. The <a href="https://people.freedesktop.org/~dbn/pkg-config-guide.html">pkg-config</a> documentation is fairly terse,
and even <a href="http://pkgconf.org/">pkgconf</a> hasn’t improved on that.</p>
<h3>The problem</h3>
<p>Let’s assume you maintain a project that has a dependency using pkg-config.</p>
<p>Let’s also assume that the project you are depending on loads some files
from a system path, and your project plans to install some files under that path.</p>
<p>The questions are:</p>
<ul>
<li>how can the project you are depending on provide an appropriate way for
you to discover where that path is</li>
<li>how can the project you maintain use that information</li>
</ul>
<p>The answer to both questions is: <em>by using variables in the pkg-config file</em>.
Sadly, there’s still some confusion as to how those variables work, so this
is my attempt at clarifying the issue.</p>
<h3>Defining variables in pkg-config files</h3>
<p>The typical preamble stanza of a pkg-config file is something like this:</p>
<div class="highlight"><pre><span></span><code>prefix=/some/prefix
libdir=<span class="cp">${</span><span class="n">prefix</span><span class="cp">}</span>/lib
datadir=<span class="cp">${</span><span class="n">prefix</span><span class="cp">}</span>/share
includedir=<span class="cp">${</span><span class="n">prefix</span><span class="cp">}</span>/include
</code></pre></div>
<p>Each variable can reference other variables; for instance, in the example
above, all the other directories are relative to the <code>prefix</code> variable.</p>
<p>Those variables that can be extracted via pkg-config itself:</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>pkg-config<span class="w"> </span>--variable<span class="o">=</span>includedir<span class="w"> </span>project-a
/some/prefix/include
</code></pre></div>
<p>As you can see, the <code>--variable</code> command line argument will automatically
expand the <code>${prefix}</code> token with the content of the <code>prefix</code> variable.</p>
<p>Of course, you can define any and all variables inside your own pkg-config
file; for instance, this is the definition of the <code>giomoduledir</code> variable
inside the <code>gio-2.0</code> pkg-config file:</p>
<div class="highlight"><pre><span></span><code>prefix=/usr
libdir=<span class="cp">${</span><span class="n">prefix</span><span class="cp">}</span>/lib
…
giomoduledir=<span class="cp">${</span><span class="n">libdir</span><span class="cp">}</span>/gio/modules
</code></pre></div>
<p>This way, the <code>giomoduledir</code> variable will be expanded to
<code>/usr/lib/gio/modules</code> when asking for it.</p>
<blockquote>
<p>If you are defining a path inside your project’s pkg-config file,
<strong>always</strong> make sure you’re using a relative path!</p>
</blockquote>
<p>We’re going to see why this is important in the next section.</p>
<h3>Using variables from pkg-config files</h3>
<p>Now, this is where things get complicated.</p>
<p>As I said above, pkg-config will expand the variables using the definitions
coming from the pkg-config file; so, in the example above, getting the
<code>giomoduledir</code> will use the prefix provided by the <code>gio-2.0</code> pkg-config
file, which is the prefix into which <span class="caps">GIO</span> was installed. This is all well and
good if you just want to know where <span class="caps">GIO</span> installed its own modules, in the
same way you want to know where its headers are installed, or where the
library is located.</p>
<p>What happens, though, if your own project needs to install <span class="caps">GIO</span> modules in a
shared location? More importantly, what happens if you’re building your
project in a separate prefix?</p>
<p>If you’re thinking: “I should install it into the same location as specified
by the <span class="caps">GIO</span> pkg-config file”, think again. What happens if you are building
against the system’s <span class="caps">GIO</span> library? The prefix into which it has been
installed is only going to be accessible by the administrator user; or it
could be on a read-only volume, managed by libostree, so <code>sudo</code> won’t save you.</p>
<p>Since you’re using a separate prefix, you really want to install the files
provided by your project under the prefix used to configure your project.
That does require knowing all the possible paths used by your dependencies,
hard coding them into your own project, and ensuring that they never change.</p>
<p>This is clearly not great, and it places additional burdens on your role as
a maintainer.</p>
<p>The correct solution is to tell pkg-config to expand variables using your
own values:</p>
<div class="highlight"><pre><span></span><code>$<span class="w"> </span>pkg-config<span class="w"> </span><span class="se">\</span>
><span class="w"> </span>--define-variable<span class="o">=</span><span class="nv">prefix</span><span class="o">=</span>/your/prefix<span class="w"> </span><span class="se">\</span>
><span class="w"> </span>--variable<span class="o">=</span>giomoduledir
><span class="w"> </span>gio-2.0
/your/prefix/lib/gio/modules
</code></pre></div>
<p>This lets you rely on the paths as defined by your dependencies, and does
not attempt to install files in locations you don’t have access to.</p>
<h3>Build systems</h3>
<p>How does this work, in practice, when building your own software?</p>
<p>If you’re using <a href="http://mesonbuild.com">Meson</a>, you can use the
<code>get_pkgconfig_variable()</code> method of the <code>dependency</code> object, making sure to
replace variables:</p>
<div class="highlight"><pre><span></span><code><span class="n">gio_dep</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">dependency</span><span class="p">(</span><span class="s1">'gio-2.0'</span><span class="p">)</span>
<span class="n">giomoduledir</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">gio_dep</span><span class="o">.</span><span class="n">get_pkgconfig_variable</span><span class="p">(</span>
<span class="w"> </span><span class="s1">'giomoduledir'</span><span class="p">,</span>
<span class="w"> </span><span class="n">define_variable</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="s1">'libdir'</span><span class="p">,</span><span class="w"> </span><span class="n">get_option</span><span class="p">(</span><span class="s1">'libdir'</span><span class="p">)</span><span class="w"> </span><span class="p">],</span>
<span class="p">)</span>
</code></pre></div>
<p>This is the equivalent of the <code>--define-variable/--variable</code> command line arguments.</p>
<p>If you are using Autotools, sadly, the <code>PKG_CHECK_VAR</code> m4 macro won’t be
able to help you, because it does not allow you to expand variables. This
means you’ll have to deal with it in the old fashioned way:</p>
<div class="highlight"><pre><span></span><code><span class="n">giomoduledir</span><span class="o">=</span><span class="err">`</span><span class="o">$</span><span class="n">PKG_CONFIG</span><span class="w"> </span><span class="o">--</span><span class="n">define</span><span class="o">-</span><span class="n">variable</span><span class="o">=</span><span class="n">libdir</span><span class="o">=$</span><span class="n">libdir</span><span class="w"> </span><span class="o">--</span><span class="n">variable</span><span class="o">=</span><span class="n">giomoduledir</span><span class="w"> </span><span class="n">gio</span><span class="o">-</span><span class="mf">2.0</span><span class="err">`</span>
</code></pre></div>
<p>Which is annoying, and yet another reason why you should move off from
Autotools and to Meson. 😃</p>
<h3>Caveats</h3>
<p>All of this, of course, works <strong>only</strong> if paths are expressed as locations
relative to other variables. If that does not happen, you’re going to have a
bad time. You’ll still get the variable as requested, but you won’t be able
to make it relative to your prefix.</p>
<p>If you maintain a project with paths expressed as variables in your
pkg-config file, check them now, and make them relative to existing
variables, like <code>prefix</code>, <code>libdir</code>, or <code>datadir</code>.</p>
<p>If you’re using Meson to generate your pkg-config file, make sure that the
paths are relative to other variables, and file bugs if they aren’t.</p>Recipes hackfest2018-03-02T01:50:00+01:002023-02-20T00:36:55+00:00ebassitag:www.bassi.io,2018-03-02:/articles/2018/03/02/recipes-hackfest-2018/<p>In which I recap the Recipes hackfest held in Yogyakarta</p><p>The Recipes application started as a celebration of <span class="caps">GNOME</span>’s community and
history, and it’s grown to be a great showcase for what <span class="caps">GNOME</span> is about:</p>
<ul>
<li>design guidelines and attention to detail</li>
<li>a software development platform for modern applications</li>
<li>new technologies, strongly integrated with the <span class="caps">OS</span></li>
<li>people-centered development</li>
</ul>
<p>Additionally, Recipes has become a place where to iterate design and
technology for the rest of the <span class="caps">GNOME</span> applications.</p>
<p>Nevertheless, while design patterns, toolkit features, Flatpak and portals,
are part of the development experience, without content provided by the
people using Recipes there would not be an application to begin with.</p>
<p>If we look at the work Endless has been doing on its own
<a href="https://endlessm.github.io/eos-knowledge-lib">framework</a> for content-driven applications, there’s a
natural fit — which is why I was really happy to attend the <a href="https://wiki.gnome.org/Hackfests/Recipes2018">Recipes
hackfest</a> in Yogyakarta, this week.</p>
<p><figure>
<figcaption class="image-caption">
<p>Fried Jawanese noodle make a healty breakfast</p>
</figcaption>
<div><img src="https://www.bassi.io/images/fried-jawanese-noodles.jpg"/></div>
</figure></p>
<p>In the Endless framework we take structured data — like a web page, or a <span class="caps">PDF</span>
document, or a mix of video and text — and we construct “shards”, which
embed both the content, its metadata, and a <a href="http://xapian.org">Xapian</a> database that
can be used for querying the data. We take the shards and distribute them
though <a href="https://flatpak.org">Flatpak</a> as a runtime extension for our applications, which
means we can take advantage of Flatpak for shipping updates efficiently.</p>
<p>During the hackfest we talked about how to take advantage of the data model
Endless applications use, as well as its distribution model; instead of
taking tarballs with the recipe text, the images, and the metadata attached
to each, we can create shards that can be mapped to a custom data model.
Additionally, we can generate those shards locally when exporting the
recipes created by new chefs, and easily re-integrate them with the shared
recipe shards — with the possibility, in the future, to have a whole web
application that lets you submit new recipes, and the maintainers review
them without necessarily going through Matthias’s email. 😉</p>
<p>The data model discussion segued into how to display that data. The Endless
framework has the concept of <a href="https://vimeo.com/120858063">cards</a>, which are context-aware
data views; depending on context, they can have more or less details exposed
to the user — and all those details are populated from the data model
itself. Recipes has custom widgets that do a very similar job, so we talked
about how to create a shared layer that can be reused both by Endless
applications and by <span class="caps">GNOME</span> applications.</p>
<p><figure>
<figcaption class="image-caption">
<p>Sadly, I don’t remember the name of this soup, only that it had chicken hearts in it, and that Cosimo loved it</p>
</figcaption>
<div><img src="https://www.bassi.io/images/unnamed-soup.jpg"/></div>
</figure></p>
<p>At the end of the hackfest we were able to have a proof of concept of
Recipes loading the data from a custom shard, and using the Endless
framework to display it; translating that into shareable code and libraries
that can be used by other projects is the next step of the roadmap.</p>
<p>All of this, of course, will benefit more than just the Recipes application.
For instance, we could have a Dictionary application that worked offline,
and used Wiktionary as a source, and allowed better queries than just
substring matching; we could have applications like Photos and Documents
reuse the same <span class="caps">UI</span> elements as Recipes for their collection views; Software
and Recipes already share a similar “landing page” design (and widgets),
which means that Software could also use the “card” <span class="caps">UI</span> elements.</p>
<p>There’s lots for everyone to do, but exciting times are ahead!</p>
<p><figure>
<figcaption class="image-caption">
<p>And after we’re done we can relax by the pool</p>
</figcaption>
<div><img src="https://www.bassi.io/images/poolside.jpg"/></div>
</figure></p>
<hr>
<p>I’d be remiss if I didn’t thank our hosts at the <a href="http://www.amikom.ac.id">Amikom university</a>.</p>
<p>Yogyakarta is a great city; I’ve never been in Indonesia before, and I’ve
greatly enjoyed my time here. There’s lots to see, and I strongly recommend
visiting. I’ve loved the food, and the people’s warmth.</p>
<p>I’d like to thank my employer, Endless, for letting me take some time to
attend the hackfest; and the <span class="caps">GNOME</span> Foundation, for sponsoring my travel.</p>
<p><figure>
<figcaption class="image-caption">
<p>The travelling Wilber</p>
</figcaption>
<div><img src="https://www.bassi.io/images/wilber-in-motion.jpg"/></div>
</figure></p>
<hr>
<div style="text-align:center">
<img class="center" src="https://www.bassi.io/images/sponsored-badge-simple.png" alt="Sponsored by the GNOME Foundation"/>
</div>GLib tools rewrite2017-10-13T12:39:00+01:002017-10-13T16:21:00+01:00ebassitag:www.bassi.io,2017-10-13:/articles/2017/10/13/glib-tools/<p class="first last">In which I make a public service announcement about the small utilities provided by GLib</p>
<p>You can safely skip this article if you’re not building software using
enumeration types and signal handlers; or if you’re already using <a class="reference external" href="http://mesonbuild.com">Meson</a>.</p>
<p>For more that 15 years, GLib has been shipping with two small utilities:</p>
<ul class="simple">
<li><tt class="docutils literal"><span class="pre">glib-mkenums</span></tt>, which scans a list of header files and generates <tt class="docutils literal">GEnum</tt>
and <tt class="docutils literal">GFlags</tt> types out of them, for use in GObject properties and signals</li>
<li><tt class="docutils literal"><span class="pre">glib-genmarshal</span></tt>, which reads a file containing a description of marshaller
functions, and generates C code for you to use when declaring signals</li>
</ul>
<p>If you update to GLib 2.54, released in September 2017, you may notice that the
<tt class="docutils literal"><span class="pre">glib-mkenums</span></tt> and <tt class="docutils literal"><span class="pre">glib-genmarshal</span></tt> tools have become sligly more verbose and
slightly more strict about their input.</p>
<p>During the 2.54 development cycle, both utilities have been rewritten in Python
from a fairly ancient Perl, in the case of <tt class="docutils literal"><span class="pre">glib-mkenums</span></tt>; and from C, in the
case of <tt class="docutils literal"><span class="pre">glib-genmarshal</span></tt>. This port was done to address the proliferation of
build time dependencies on GLib; the cross-compilation hassle of having a small
C utility being built and used during the build; and the move to Meson as the
default (and hopefully only) build system for future versions of GLib. Plus, the
port introduced colorised output, and we all know everything looks better with colors.</p>
<p>Sadly, none of the behaviours and expected input or output of both tools have
ever been documented, specified, or tested in any way. Additionally, it turns
out that lots of people either figured out how to exploit undefined behaviour,
or simply cargo-culted the use of these tools into their own project. This is
entirely on us, and I’m going to try and provide <a class="reference external" href="https://bugzilla.gnome.org/show_bug.cgi?id=788948">better documentation</a> to both
tools in the form of a decent man page, with examples of integration inside
Autotools-based projects.</p>
<p>In the interest of keeping old projects building, both utilities will try to
replicate the undefined behaviours as much as possible, but now you’ll get a
warning instead of the silent treatment, and maybe you’ll get a chance at
fixing your build.</p>
<p>If you are maintaining a project using those two utilities, these are the
things to watch out for, and ideally to fix by strictly depending on
GLib ≥ 2.54.</p>
<div class="section" id="glib-genmarshal">
<h2>glib-genmarshal</h2>
<ul>
<li><p class="first">if you’re using <tt class="docutils literal"><span class="pre">glib-genmarshal</span> <span class="pre">--header</span> <span class="pre">--body</span></tt> to avoid the “missing
prototypes” compiler warning when compiling the generated marshallers
source file, please switch to using <tt class="docutils literal"><span class="pre">--prototypes</span> <span class="pre">--body</span></tt>. This will
ensure you’ll get <strong>only</strong> the prototypes in the source file, instead of
a whole copy of the header.</p>
</li>
<li><p class="first">Similarly, if you’re doing something like the stanza below in order to
include the header inside the body:</p>
<pre class="literal-block">
foo-marshal.h: foo-marshal.list Makefile
$(AM_V_GEN) \
$(GLIB_GENMARSHAL) --header foo-marshal.list \
> foo-marshal.h
foo-marshal.c: foo-marshal.h
$(AM_V_GEN) (
echo '#include "foo-marshal.h"' ; \
$(GLIB_GENMARSHAL) --body foo-marshal.list \
) > foo-marshal.c
</pre>
<p>you can use the newly added <tt class="docutils literal"><span class="pre">--include-header</span></tt> command line argument, instead.</p>
</li>
<li><p class="first">The stanza above has also been used to inject <tt class="docutils literal">#define</tt> and <tt class="docutils literal">#undef</tt>
pre-processor directives; these can be replaced with the <tt class="docutils literal"><span class="pre">-D</span></tt> and
<tt class="docutils literal"><span class="pre">-U</span></tt> newly added command line arguments, which work just like the
<span class="caps">GCC</span> ones.</p>
</li>
<li><p class="first">This is not something that came from the Python port, as it’s been
true since the inclusion of <tt class="docutils literal"><span class="pre">glib-genmarshal</span></tt> in GLib, <strong>17 years ago</strong>:
the <tt class="docutils literal"><span class="caps">NONE</span></tt> and <tt class="docutils literal"><span class="caps">BOOL</span></tt> tokens are deprecated, and should <em>not</em> be
used; use <tt class="docutils literal"><span class="caps">VOID</span></tt> and <tt class="docutils literal"><span class="caps">BOOLEAN</span></tt>, respectively. The new version of
<tt class="docutils literal"><span class="pre">glib-genmarshal</span></tt> will now properly warn about this, instead of just
silently converting them, and never letting you know you should fix
your marshal.list file.</p>
</li>
</ul>
<p>If you want to silence all messages outside of errors, you can now use the
<tt class="docutils literal"><span class="pre">--quiet</span></tt> command line option; conversely, use <tt class="docutils literal"><span class="pre">--verbose</span></tt> if you want
to get more messages.</p>
</div>
<div class="section" id="glib-mkenums">
<h2>glib-mkenums</h2>
<p>The <tt class="docutils literal"><span class="pre">glib-mkenums</span></tt> port has been much more painful than the marshaller
generator one; mostly, because there are many, many more ways to screw up
code generation when you have command line options and file templates, and
mostly because the original code base relied heavily on Perl behaviour and
side effects. Cargo culting Autotools stanzas is also much more of a thing
when it comes to enumerations than marshallers, apparently. Imagine what
we could achieve if the tools that we use to build our code didn’t actively
work against us.</p>
<ul>
<li><p class="first">First of all, try and avoid having mixed encoding inside source code
files that are getting parsed; mixing Unicode and <span class="caps">ISO</span>-8859 encoding is
not a great plan, and C does not have a way to specify the encoding
to begin with. Yes, you may be doing that inside comments, so who
cares? Well, <em>a tool that parses comments might</em>.</p>
</li>
<li><p class="first">If you’re mixing template files with command line arguments for some
poorly thought-out reason, like this:</p>
<pre class="literal-block">
foo-enums.h: foo-enums.h.in Makefile
$(AM_V_GEN) $(GLIB_MKENUMS) \
--fhead '#ifdef FOO_ENUMS_H' \
--fhead '#defineFOO_ENUMS_H' \
--template foo-enums.h.in \
--ftail '#endif /* FOO_ENUMS_H */' \
> foo-enums.h
</pre>
<p>the old version of <tt class="docutils literal"><span class="pre">glib-mkenums</span></tt> would basically build templates
depending on the phase of the moon, as well as some internal detail
of how Perl works. The new tool has a specified order:</p>
<ul class="simple">
<li>the <tt class="docutils literal"><span class="caps">HEAD</span></tt> stanzas specified on the command line are always prepended
to the template file</li>
<li>the <tt class="docutils literal"><span class="caps">PROD</span></tt> stanzas specified on the command line are always appended
to the template file</li>
<li>the <tt class="docutils literal"><span class="caps">TAIL</span></tt> stanzas specified on the command line are always appended
to the template file</li>
</ul>
</li>
</ul>
<p>Like with <tt class="docutils literal"><span class="pre">glib-genmarshal</span></tt>, the <tt class="docutils literal"><span class="pre">glib-mkenums</span></tt> tool also tries to be
more verbose in what it expects.</p>
<hr class="docutils" />
<p>Ideally, by this point, you should have switched to <a class="reference external" href="http://mesonbuild.com">Meson</a>, and you’re now
using a sane build system that generates this stuff for you.</p>
<p>If you’re still stuck with Autotools, though, you may also want to consider
dropping <tt class="docutils literal"><span class="pre">glib-genmarshal</span></tt>, and use the <span class="caps">FFI</span>-based generic marshaller in
your signal definitions — which comes at a small performance cost, but if
you’re putting signal emission inside a performance-critical path you should
just be ashamed of yourself.</p>
<p>For enumerations, you could use something like <a class="reference external" href="https://github.com/endlessm/xapian-glib/blob/master/xapian-glib/xapian-enums.cc#L22">this macro</a>, which I tend
to employ in all my projects with just few, small enumeration types, and
where involving a whole separate pass at parsing C files is kind of overkill.
Ideally, GLib <a class="reference external" href="https://bugzilla.gnome.org/show_bug.cgi?id=627241">would ship its own version</a>, so maybe it’ll be replaced
in a new version.</p>
<hr class="docutils" />
<p>Many thanks to Jussi Pakkanen, Nirbheek Chauhan, Tim-Philipp Müller, and
Christoph Reiter for the work on porting <tt class="docutils literal"><span class="pre">glib-mkenums</span></tt>, as well as
fixing my awful Parseltongue.</p>
</div>
GUADEC 20172017-08-11T13:45:00+01:002017-08-11T14:33:00+01:00ebassitag:www.bassi.io,2017-08-11:/articles/2017/08/11/2017-guadec/<p class="first last">In which I report my activities at <span class="caps">GUADEC</span> 2017</p>
<p>Another year, another <a class="reference external" href="https://2017.guadec.org"><span class="caps">GUADEC</span></a> — my 13th to date. Definitely not getting
younger, here. 😉</p>
<p>As usual, it was great to see so many faces, old and new. Lots of faces,
as well; attendance has been really good, this year.</p>
<p>The 20th anniversary party was a blast; the venue was brilliant, and
watching people going around the tables in order to fill in slots for the
raffle tickets was hilarious. I loved every minute of it — even if the
‘90s music was an assault on my teenage years. See above, re: getting older.</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">Waiting for the <a href="https://twitter.com/hashtag/GnomeParty?src=hash">#GnomeParty</a> raffle to begin <a href="https://t.co/BdCQDgV6UZ">pic.twitter.com/BdCQDgV6UZ</a></p>— Emmanuele Bassi (@ebassi) <a href="https://twitter.com/ebassi/status/891390130900140032">July 29, 2017</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script><p>The talks were, as usual, stellar. It’s always so hard to chose from the
embarrassment of riches that is the submission pool, but every year I
think the quality of what ends up on the schedule is so high that I cannot
be sad.</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">First <a href="https://twitter.com/hashtag/guadec2017?src=hash">#guadec2017</a> keynote by <a href="https://twitter.com/o0karen0o">@o0karen0o</a> - “The Battle over our technology” <a href="https://t.co/P0WnlASxe1">pic.twitter.com/P0WnlASxe1</a></p>— Emmanuele Bassi (@ebassi) <a href="https://twitter.com/ebassi/status/890921497937584128">July 28, 2017</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script><blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">Flatpaks for the Flatpak throne! <a href="https://twitter.com/hashtag/guadec2017?src=hash">#guadec2017</a> <a href="https://t.co/RrfLRdlgGN">pic.twitter.com/RrfLRdlgGN</a></p>— Emmanuele Bassi (@ebassi) <a href="https://twitter.com/ebassi/status/890882943270440960">July 28, 2017</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script><p>Lots and lots of people were happy to see the <a class="reference external" href="https://endlessos.com/">Endless</a> contingent at the
conference; the talks from my colleagues were really well received, and
I’m sure we’re going to see even more collaboration spring from the seeds
planted this year.</p>
<hr class="docutils" />
<p>My <a class="reference external" href="https://ebassi.github.io/2017-guadec">talk</a> about continuous integration in <span class="caps">GNOME</span> was well-received, I think;
I had to speed up a bit at the end because I lost time while connecting to
the projector (not enough juice when on battery to power the <span class="caps">HDMI</span>-over-<span class="caps">USB</span>
C connector; lesson learned for the next talk). I would have liked to get
some more time to explain what I’d like to achieve with <a class="reference external" href="https://build.gnome.org">Continuous</a>.</p>
<figure>
<figcaption class="image-caption">
<p>Do not disturb the build sheriff</p>
</figcaption>
<div><img src="https://www.bassi.io/images/do-not-disturb.jpg"/></div>
</figure><p>I ended up talking with many people at the unconference days, in any case.
If you’re interested in helping out the automated build of <span class="caps">GNOME</span> components
and to improve the reliability of the project, feel free to drop by on
irc.gnome.org (or on Matrix!) in the <tt class="docutils literal">#testable</tt> channel.</p>
<hr class="docutils" />
<p>The <a class="reference external" href="https://wiki.gnome.org/GUADEC/2017/Unconference/">unconference</a> days were also very productive, for me. The <span class="caps">GTK</span>+ session
was, as usual, a great way to plan ahead for the future; last year we
defined the new <a class="reference external" href="https://blog.gtk.org/2016/09/01/versioning-and-long-term-stability-promise-in-gtk/">release cycle</a> for <span class="caps">GTK</span>+ and jump start the 4.0 development
cycle. This year we drafted a <a class="reference external" href="https://wiki.gnome.org/Projects/GTK%2B/Roadmap/GTK4">roadmap</a> with the remaining tasks.</p>
<p>I talked about Flatpak, FlatHub, Builder, performance in Mutter and <span class="caps">GNOME</span>
Shell; I wanted to attend the Rust and <span class="caps">GJS</span> sessions, but that would have
required the ability to clone myself, or be in more than one place at once.</p>
<p>During the unconference, I was also able to finally finish the <span class="caps">GDK</span>-Pixbuf
port of the build system to <a class="reference external" href="http://mesonbuild.com">Meson</a>. Testing is very much welcome, before
we bin the Autotools build and bring one of the oldest modules in <span class="caps">GNOME</span>
into the future.</p>
<p>Additionally, I was invited to the <span class="caps">GNOME</span> Release Team, mostly to deal with
the various continuous integration build issues. This, sadly, does not mean
that I’m one step closer to my ascendance as the power mad dictator of all
of <span class="caps">GNOME</span>, but it means that if there are issues with your module, you have
a more-or-less official point of contact.</p>
<hr class="docutils" />
<p>I can’t wait for <span class="caps">GUADEC</span> 2018! See you all in Almería!</p>
Dev v Ops2017-08-10T16:51:00+01:002017-08-10T18:55:00+01:00ebassitag:www.bassi.io,2017-08-10:/articles/2017/08/10/dev-v-ops/<p>In which application development and packaging are discussed, vis-a-vis old and new practices</p><p>In <a href="https://2017.guadec.org/talks-and-events/#abstract-1-resurrecting_dinosaurs_what_can_possibly_go_wrong">his talk</a> at the 2017 <span class="caps">GUADEC</span> in Manchester, Richard Brown
presented a set of objections to the current trend of new packaging systems
— mostly AppImage, Snap, and Flatpak — from the perspective of a Linux
distribution integrator.</p>
<p>I’m not entirely sure he managed to convince everybody in the attendance,
but he definitely presented a well-reasoned argument, steeped in history. I
freely admit I went in not expecting to be convinced, but fully expecting to
be engaged and I can definitely say I left the room thoroughly satisfied,
and full of questions on how we can make the application development and
distribution story on Linux much better. Talking with other people involved
with <a href="http://flatpak.org">Flatpak</a> and <a href="https://flathub.org">Flathub</a> we already identified
various places where things need to be improved, and how to set up automated
tools to ensure we don’t regress.</p>
<p>In the end, though, all I could think of in order to summarise it when
describing the presentation to people that did not attend it, was this:</p>
<blockquote>
<p>Linux distribution developer tells application and system developers that
packaging is a solved problem, as long as everyone uses the same <span class="caps">OS</span>,
distribution, tools, and becomes a distro packager.</p>
</blockquote>
<p>Which, I’m the first to admit, seems to subject the presentation to
impressive levels of lossy compression. I want to reiterate that I think
Richard’s argument was presented much better than this; even if the talk was
really doom and gloom predictions from a person who sees new technologies
encroaching in his domain, Richard had wholesome intentions, so I feel a bit
bad about condensing them into a quip.</p>
<p>Of course, this leaves me in quite a bind. It would be easy — <em>incredibly</em>
easy — to dismiss a lot of the objections and points raised by Richard as a
case of the Italian idiom “do not ask to the inn-keeper if the house wine is
good”. Nevertheless, I want to understand why those objections where made in
the first place, because it’s not going to be the last we are going to be
hearing them.</p>
<p>I’ve been turning an answer to that question in my head for a while, now,
and I think I finally came up with something that tries to rise to the level
of Richard’s presentation, in the sense that I tried to capture the issue
behind it, instead of just reacting to it.</p>
<hr>
<p>Like many things in tech, it all comes down to developers and system administators.</p>
<p>I don’t think I’m being controversial, or exposing some knowledge for
initiates, when I say that Linux distributions are not made by the people
that write the software they distribute. Of course, there are various
exceptions, with upstream developers being involved (by volunteer or more
likely paid work) with <em>a particular distribution</em> of their software, but by
and large there has been a complete disconnect between who <em>writes</em> the code
and who <em>packages</em> it.</p>
<p>Another, I hope, uncontroversial statement is that people on the Linux
distribution side of things are mostly interested in making sure that the
overall <span class="caps">OS</span> fits into a fairly specific view of how computer systems should
work: a central authority that oversees, via policies and validation tools
that implement those policies, how all the pieces fit together, up to a
certain point. There’s a name for that kind of authority: system administrators.</p>
<p>Linux distributions are the continuation of system administration policies
via other means: all installed Linux machines are viewed as part of the same
shared domain, with clear lines of responsibility and ownership that trace
from a user installation to the packager, to the people that set up the
policies of integration, and which may or may not involve the people that
make the software in the first place — after all, that’s what distro patches
are for.</p>
<p>You may have noticed that in the past 35 years the landscape of computing
has been changed by the introduction of the personal computer; that the
release of Windows 95 introduced the concept of a mass marketable operating
system; and that, by and large, there has been a complete disintermediation
between software vendors and users. A typical computer user won’t have an
administrator giving them a machine with the <span class="caps">OS</span>, validating and managing all
the updates; instead of asking an admin to buy, test, and deploy an
application for them, users went to a shop and bought a box with floppies or
an optical storage — and now they just go to online version of that shop
(typically owned by the <span class="caps">OS</span> vendor) and download it. The online store may
just provide users with the guarantee that the app won’t steal all their
money without asking in advance, but that’s pretty much where the
responsibility of the owners of the store ends.</p>
<p>Linux does not have stores.</p>
<p>You’re still supposed to go ask your sysadmin for an application to be
available, and you’re still supposed to give your application to the
sysadmin so that they can deploy it — with or without modifications.</p>
<p>Yet, in the 25 years of their history, Linux distribution haven’t managed to
convince the developers of</p>
<ul>
<li>Perl</li>
<li>Python</li>
<li>Ruby</li>
<li>JavaScript</li>
<li>Rust</li>
<li>Go</li>
<li><span class="caps">PHP</span></li>
<li><code>insert_your_language_here</code></li>
</ul>
<p>applications to defer all their dependency handling and integration to
distro packagers.</p>
<p>They have just about managed to convince C and C++ developers, because the
practices of those languages are so old and entrenched, the tools so poor,
and because they share part of the same heritage; and TeX writers, for some
weird reason, as you can witness by looking at how popular distribution
package all the <code>texlive</code> modules.</p>
<p>The crux is that nobody, on any existing major (≥ 5% of market penetration)
platform, develops applications like Linux distributors want them to. Nobody
<strong>wants to</strong>. Not even the walled gardens you keep in your pocket and use to
browse the web, watch a video, play a game, and occasionally make a phone
call, work like that, and those are the closest thing to a heavily managed
system you can get outside of a data center.</p>
<p>The issue is not the “managed by somebody” part; the issue is the inevitable
intermediary between an application developer and an application user.</p>
<p>Application developers want to be able to test and have a reproducible
environment, because it makes it easier for them to find bugs and to ensure
that their project works as they intented; the easiest way to do that is to
have people literally use the developer’s computer — this is why web
applications deployed on top of a web browser engine that consumes all your
<span class="caps">CPU</span> cores in a fiery pit are eating everybody’s lunch; or because software
as a service even exists. The closest thing application developers have
found to ship their working laptop to the users of our applications without
physically shipping hardware, is to give them a read-only file system image
that we have built ourselves, or a list of dependencies hosted on a public
source code repository that the build system will automatically check out
prior to deploying the application.</p>
<p>The Linux distribution model is to have system administrators turned
packagers control all the dependencies and the way they interact on a
system; check all the licensing terms and security issues, when not
accidentally introducing them; and then fight among themselves on the
practicalities and ideologies of how that software should be distributed,
installed, and managed.</p>
<p>The more I think about it, the less I understand how that ever worked in the
first place. It is not a mystery, though, why it’s a dying model.</p>
<p>When I say that “nobody develops applications like the Linux distributions
encourages and prefers” I’m not kidding around: Windows, macOS, iOS,
Electron, and Android application developers are heavily based on the
concept of a core set of <span class="caps">OS</span> services; a parallel installable blocks of
system dependencies shipped and retired by the <span class="caps">OS</span> vendor; and a bundling
system that allows application developers to provide their own dependencies,
and control them.</p>
<p>Sounds familiar?</p>
<p>If it does, it’s becase, in the past 25 years, every other platform (and I
include programming languages with a fairly comprehensive standard library
in that definition, not just operating systems) has implemented something
like this — even in free and open source software, where this kind of
invention mostly exists both as a way to replicate Linux distributions on
Windows, and to route around Linux distributions on Linux.</p>
<p>It should not come as a surprise that there’s going to be friction; while
for the past two decades architects of both operating systems and
programming languages have been trying to come up with a car, Linux
distributions have been investing immeasurable efforts in order to come up
with a jet fueled, <span class="caps">SRB</span>-augmented horse. Sure: it’s so easy to run <code>apt
install foo</code> and get <code>foo</code> installed. How did <code>foo</code> get into the repository?
How can you host a repository, if you can’t, or don’t want to host it on
somebody else’s infrastructure? What happens when you have to deal with a
bajillion, slightly conflicting, ever changing policies? How do you keep
your work up to date for everyone, and every combination? What happens if
you cannot give out the keys to your application to everyone, even if the
application itself may be free software?</p>
<p>Scalability is the problem; too many intermediaries, too many gatekeepers.
Even if we had a single one, that’s still one too many. People using
computers expect to access whatever application they need, at the touch of a
finger or at the click of a pointer; if they cannot get to something in time
for the task they have to complete, they will simply leave and never come
back. Sure, they can probably appreciate the ease of installing 30 different
console text editors, 25 <span class="caps">IRC</span> clients, and 12 email clients, all in various
state of disrepair and functionality; it won’t really mean much, though,
because they will be using something else by that time.</p>
<p>Of course, now we in the Linux world are in the situation of reimplementing
the past 20 years of mistakes other platforms have made; of course, there
will be growing pains, and <strong>maybe</strong>, if we’re careful enough, we can
actually learn for somebody else’s blunders, and avoid falling into common
traps. We’re going to have new and exciting traps to fall into!</p>
<p>Does this mean it’s futile, and that we should just give up on everything
and just go back to our comfort zone? If we did, it would not only be a
disservice to our existing users, but also to the users of every other
platform. Our — and I mean the larger free software ecosystem — proposition
is that we wish all users to have the tools to modify the software they are
using; to ensure that the software in question has not been modified against
their will or knowledge; and to access their own data, instead of merely
providing it to third parties and renting out services with it. We should
have fewer intermediaries, not more. We should push for adoption and access.
We should provide a credible alternative to other platforms.</p>
<p>This will not be easy.</p>
<p>We will need to grow up a lot, and in little time; adopt better standards
than just “it builds on my laptop” or “it works if you have been in the
business for 15 years and know all the missing stairways, and by the way,
isn’t that a massive bear trap covered with a tarpaulin on the way to the
goal”. Complex upstream projects will have to start caring about things like
reproducibility; licensing; security updates; continuous integration; <span class="caps">QA</span> and
validation. We will need to care about stable system services, and backward
compatibility. We will not be shielded by a third party any more.</p>
<p>The good news is: we have a lot of people that know about this stuff, and we
can ask them how to make it work. We can take existing tools and make them
generic and part of our build pipeline, instead of having them inside silos.
We can adopt shared policies upstream instead of applying them downstream,
and twisting software to adapt to all of them.</p>
<p>Again, this won’t be easy.</p>
<p>If we wanted easy, though, we would not be making free and open source
software for everyone.</p>Further experiments in Meson2017-05-19T18:20:00+01:002017-05-19T18:23:43+01:00ebassitag:www.bassi.io,2017-05-19:/articles/2017/05/19/further-experiments-in-meson/<p>in which more components gets ported to Meson</p><p><a href="http://mesonbuild.com">Meson</a> is definitely getting more traction in <span class="caps">GNOME</span> (and other
projects), with many components adding support for it in parallel to
autotools, or outright switching to it. There are still bugs, here and
there, and we definitely need to improve build environments — like
Continuous — to support Meson out of the box, but all in all I’m really
happy about not having to deal with autotools any more, as well as being
able to build the G* stack much more quickly when doing continuous integration.</p>
<p>Now that <span class="caps">GTK</span>+ has added Meson support, though, it’s time to go through the
dependency chain in order to clean up and speed up the build in the lower
bits of our stack. After an aborted attempt at porting GdkPixbuf, I decided
to <a href="https://git.gnome.org/browse/pango/log/?h=wip/meson">port Pango</a>.</p>
<p>All in all, Pango proved to be an easy win; it took me about one day to port
from Autotools to Meson, and most of it was mechanical translation from
weird autoconf/automake incantations that should have been removed years
ago<sup id="fnref:1"><a class="footnote-ref" href="#fn:1">1</a></sup>. Most of the remaining bits were:</p>
<ul>
<li>ensuring that both Autotools and Meson would build the same
DSOs, with the same symbols</li>
<li>generating the same introspection data and documentation</li>
<li>installing tests and data in the appropriate locations</li>
</ul>
<p>Thanks to the ever vigilant eye of <a href="http://blog.nirbheek.in/">Nirbheek Chauhan</a>, and
thanks to the new <a href="http://mesonbuild.com/Reference-manual.html">Meson reference</a>, I was also able to
make the Meson build slightly more idiomatic than a straight, 1:1 port would
have done.</p>
<p>The results are a full Meson build that takes about the same time as
<code>./autogen.sh</code> to run:</p>
<div class="highlight"><pre><span></span><code><span class="o">*</span><span class="w"> </span><span class="n">autogen</span><span class="o">.</span><span class="n">sh</span><span class="p">:</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">meson</span>
<span class="w"> </span><span class="n">real</span><span class="w"> </span><span class="mi">0</span><span class="n">m11</span><span class="o">.</span><span class="mi">149</span><span class="n">s</span><span class="w"> </span><span class="n">real</span><span class="w"> </span><span class="mi">0</span><span class="n">m2</span><span class="o">.</span><span class="mi">525</span><span class="n">s</span>
<span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="mi">0</span><span class="n">m8</span><span class="o">.</span><span class="mi">153</span><span class="n">s</span><span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="mi">0</span><span class="n">m1</span><span class="o">.</span><span class="mi">609</span><span class="n">s</span>
<span class="w"> </span><span class="n">sys</span><span class="w"> </span><span class="mi">0</span><span class="n">m2</span><span class="o">.</span><span class="mi">363</span><span class="n">s</span><span class="w"> </span><span class="n">sys</span><span class="w"> </span><span class="mi">0</span><span class="n">m1</span><span class="o">.</span><span class="mi">206</span><span class="n">s</span>
<span class="o">*</span><span class="w"> </span><span class="n">make</span><span class="w"> </span><span class="o">-</span><span class="n">j</span><span class="o">$</span><span class="p">((</span><span class="o">$</span><span class="p">(</span><span class="n">nproc</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">2</span><span class="p">))</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">ninja</span>
<span class="w"> </span><span class="n">real</span><span class="w"> </span><span class="mi">0</span><span class="n">m9</span><span class="o">.</span><span class="mi">186</span><span class="n">s</span><span class="w"> </span><span class="n">real</span><span class="w"> </span><span class="mi">0</span><span class="n">m3</span><span class="o">.</span><span class="mi">387</span><span class="n">s</span>
<span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="mi">0</span><span class="n">m16</span><span class="o">.</span><span class="mi">295</span><span class="n">s</span><span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="mi">0</span><span class="n">m6</span><span class="o">.</span><span class="mi">887</span><span class="n">s</span>
<span class="w"> </span><span class="n">sys</span><span class="w"> </span><span class="mi">0</span><span class="n">m5</span><span class="o">.</span><span class="mi">337</span><span class="n">s</span><span class="w"> </span><span class="n">sys</span><span class="w"> </span><span class="mi">0</span><span class="n">m1</span><span class="o">.</span><span class="mi">318</span><span class="n">s</span>
<span class="o">--------------------------------------------------------------</span>
<span class="o">*</span><span class="w"> </span><span class="n">autotools</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">meson</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">ninja</span>
<span class="w"> </span><span class="n">real</span><span class="w"> </span><span class="mi">0</span><span class="n">m27</span><span class="o">.</span><span class="mi">669</span><span class="n">s</span><span class="w"> </span><span class="n">real</span><span class="w"> </span><span class="mi">0</span><span class="n">m5</span><span class="o">.</span><span class="mi">772</span><span class="n">s</span>
<span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="mi">0</span><span class="n">m45</span><span class="o">.</span><span class="mi">622</span><span class="n">s</span><span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="mi">0</span><span class="n">m8</span><span class="o">.</span><span class="mi">465</span><span class="n">s</span>
<span class="w"> </span><span class="n">sys</span><span class="w"> </span><span class="mi">0</span><span class="n">m10</span><span class="o">.</span><span class="mi">698</span><span class="n">s</span><span class="w"> </span><span class="n">sys</span><span class="w"> </span><span class="mi">0</span><span class="n">m2</span><span class="o">.</span><span class="mi">357</span><span class="n">s</span>
</code></pre></div>
<p>Not bad for a day’s worth of work.</p>
<p>My plan would be to merge this in the <code>master</code> branch pretty soon; I also
have a branch that <a href="https://git.gnome.org/browse/pango/log/?h=wip/meson-only">drops Autotools entirely</a>
but that can wait a cycle, as far as I’m concerned.</p>
<p>Now comes the hard part: porting libraries like GdkPixbuf, <span class="caps">ATK</span>,
gobject-introspection, and GLib to Meson. There’s already a GLib port,
courtesy of <a href="http://www.centricular.com/">Centricular</a>, but it needs further testing;
GdkPixbuf is pretty terrible, since it’s a <em>really</em> old library; I don’t
expect <span class="caps">ATK</span> and GObject introspection to be complicated, but the latter has a
non-recursive Make layout that is full of bees.</p>
<p>It would be nice to get to <a href="https://2017.guadec.org"><span class="caps">GUADEC</span></a> and have the
whole G* stack build with Meson and Ninja. If you want to help out, reach
out in <code>#gtk+</code>, on <span class="caps">IRC</span> or on Matrix.</p>
<div class="footnote">
<hr>
<ol>
<li id="fn:1">
<p>The Windows support still checks for <span class="caps">GCC</span> 2.x or 3.x flags, for
instance. <a class="footnote-backref" href="#fnref:1" title="Jump back to footnote 1 in the text">↩</a></p>
</li>
</ol>
</div>On Vala2017-02-13T13:12:00+00:002023-02-20T00:36:03+00:00ebassitag:www.bassi.io,2017-02-13:/articles/2017/02/13/on-vala/<p>In which I look at the state of Vala and hope for some introspection to happen</p><p>It seems I raised a bit of a stink on Twitter last week:</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr"><span class="caps">PSA</span>: if you want to write a new <a href="https://twitter.com/gnome">@gnome</a> application, don't use Vala; if you're already using it, consider porting to a non-dead language.</p>— Emmanuele Bassi (@ebassi) <a href="https://twitter.com/ebassi/status/827482509982195712">February 3, 2017</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>Of course, and with reason, I’ve been called out on this by various people.
Luckily, it was on Twitter, so we haven’t seen articles on Slashdot and
Phoronix and <span class="caps">LWN</span> with headlines like “<span class="caps">GNOME</span> developer says Vala is dead and
will be removed from all servers for all eternity and you all suck”. At
least, I’ve only seen a bunch of comments on Reddit about this, but nobody
cares about that particular cesspool of humanity.</p>
<p>Sadly, 140 characters do not leave any room for nuance, so maybe I should
probably clarify what I wrote on a venue with no character limit.</p>
<p>First of all, I’d like to apologise to people that felt I was attacking them
or their technical choices: it was not my intention, but see above, re:
character count. I may have only about 1000 followers on Twitter, but it
seems that the network effect is still a bit greater than that, so I should
be careful when wording opinions. I’d like to point out that it’s my private
Twitter account, and you can only get to what it says if you follow me, or
if you follow people who follow me and decide to retweet what I write.</p>
<p>My <span class="caps">PSA</span> was intended as a reflection on the state of Vala, and its impact on
the <span class="caps">GNOME</span> ecosystem in terms of newcomers, from the perspective of a person
that used Vala for his own personal projects; recommended Vala to newcomers;
and has to deal with the various build issues that arise in <span class="caps">GNOME</span> because
something broke in Vala or in projects using Vala. If you’re using Vala
outside of <span class="caps">GNOME</span>, you have two options: either ignore all I’m saying, as it
does not really apply to your case; or do a bit of soul searching, and see
if what I wrote <strong>does</strong> indeed apply to you.</p>
<p>First of all, I’d like to qualify my assertion that Vala is a “dead
language”. Of course people see activity in the <a href="https://git.gnome.org/browse/vala">Git repository</a>,
see the recent commits and think “the project is still alive”. Recent
commits do not tell a complete story.</p>
<p>Let’s look at the project history for the past 10 cycles (roughly 2.5
years). These are the commits for every cycle, broken up in two values: one
for the full repository, the other one for the whole repository <em>except</em> the
<code>vapi</code> directory, which contains the <span class="caps">VAPI</span> files for language bindings:</p>
<p><img alt="Commits" src="https://www.bassi.io/images/vala-repo-commits.png"></p>
<p>Aside from the latest cycle, Vala has seen very little activity; the project
itself, if we exclude binding updates, has seen less than 100 commits for
every cycle — some times even far less. The latest cycle is a bit of an
outlier, but we can notice a pattern of very little work for two/three
cycles, followed by a spike. If we look at the currently in progress cycle,
we can already see that the number of commits has decreased back to 55/42,
as of this morning.</p>
<p><img alt="Commits" src="https://www.bassi.io/images/vala-repo-commits-2.png"></p>
<p>Number of commits is just a metric, though; more important is the number of
contributors. After all, small, incremental changes may be a good thing in a
language — though, spoiler alert: they are usually an indication of a series
of larger issues, and we’ll come to that point later.</p>
<p>These are the number of developers over the same range of cycles, again
split between committers to the full repository and to the full repository
minus the <code>vapi</code> directory:</p>
<p><img alt="Developers" src="https://www.bassi.io/images/vala-repo-developers.png"></p>
<p>As you can see, the number of authors of changes is mostly stable, but still
low. If we have few people that actively commit to the repository it means
we have few people that can review a patch. It means patches linger longer
and longer, while reviewers go through their queues; it means that
contributors get discouraged; and, since nobody is paid to work full time on
Vala, it means that any interruption caused by paid jobs will be a
bottleneck on the project itself.</p>
<p>These concerns are not unique of a programming language: they exist for
every volunteer-driven free and open source project. Programming languages,
though, like core libraries, are problematic because any bottleneck causes
ripple effects. You can take any stalled project you depend on, and vendor
it into your own, but if that happens to the programming language you’re
using, then you’re pretty much screwed.</p>
<p>For these reasons, we should also look at how well-distributed is the
workload in Vala, i.e. which percentage of the work is done by the authors
of those commits; the results are <strong>not</strong> encouraging. Over that range of
cycles, Only two developers routinely crossed the 5% of commits:</p>
<ul>
<li>Rico Tzschichholz</li>
<li>Jürg Billeter</li>
</ul>
<p>And Rico has been the only one to consistently author >50% of the commits.
This means there’s only one person dealing with the project on a day to day basis.</p>
<p>As the maintainer of a project who basically had to do all the work, I
cannot even begin to tell you how soul-crushing that can become. You get
burned out, and you feel responsible for everyone using your code, and then
you get burned out some more. I honestly don’t want Rico to burn out, and
you shouldn’t, either.</p>
<p>So, let’s go into unfair territory. These are the commits for
<a href="https://www.rust-lang.org/">Rust</a> — the compiler and standard library:</p>
<p><img alt="Rust" src="https://www.bassi.io/images/rust-commits.png"></p>
<p>These are the commits for <a href="https://golang.org/">Go</a> — the compiler and base library:</p>
<p><img alt="Go" src="https://www.bassi.io/images/go-commits.png"></p>
<p>These are the commits for Vala — both compiler and bindings:</p>
<p><img alt="Vala" src="https://www.bassi.io/images/vala-commits.png"></p>
<p>These are the number of commits over the past <strong>year</strong>. Both languages are
younger than Vala, have more tools than Vala, and are more used than Vala.
<em>Of course</em>, it’s completely unfair to compare them, but those numbers
should give you a sense of scale, of what is the current high bar for a
successful programming language these days. Vala is a niche language, after
all; it’s heavily piggy-backing on the <span class="caps">GNOME</span> community because it transpiles
to C and needs a standard library and an ecosystem like the one <span class="caps">GNOME</span>
provides. I never expected Vala to rise to the level of mindshare that Go
and Rust currently occupy.</p>
<p>Nevertheless, we need to draw some conclusions about the current state of
Vala — starting from <a href="https://mail.gnome.org/archives/vala-list/2016-September/msg00001.html">this thread</a>, perhaps, as it best
encapsulates the issues the project is facing.</p>
<p>Vala, as a project, is limping along. There aren’t enough developers to
actively effect change on the project; there aren’t enough developers to
work on ancillary tooling — like build system integration, debugging and
profiling tools, documentation. Saying that “Vala compiles to C so you can
use tools meant for C” is comically missing the point, and it’s effectively
like saying that “C compiles to binary code, so you can disassemble a
program if you want to debug it”. Being able to inspect the language using
tools native to the language is a powerful thing; if you have to do the name
mangling in your head in order to set a breakpoint in <span class="caps">GDB</span> you are elevating
the barrier of contributions way above the head of many newcomers.</p>
<p>Being able to effect change means also being able to introduce change
effectively and without fear. This means things like continuous integration
and a full test suite heavily geared towards regression testing. The test
suite in Vala is made of 210 units, for a total of 5000 lines of code; the
code base of Vala (vala <span class="caps">AST</span>, codegen, C code emitter, and the compiler) is
nearly 75 thousand lines of code. There is no continuous integration,
outside of the one that <span class="caps">GNOME</span> Continuous performs when building Vala, or the
one <span class="caps">GNOME</span> developers perform when using jhbuild. Regressions are found after
days or weeks, because developers of projects using Vala update their
compiler and suddenly their projects cease to build.</p>
<p>I don’t want to minimise the enormous amount of work that every Vala
contributor brought to the project; they are heroes, all of them, and they
deserve as much credit and praise as we can give. The idea of a
project-oriented, community-oriented programming language has been
vindicated many times over, in the past 5 years.</p>
<p>If I scared you, or incensed you, then you can still blame me, and my lack
of tact. You can still call me an asshole, and you can think that I’m
completely uncool. What I do hope, though, is that this blog post pushes
you into action. Either to contribute to Vala, or to re-new your commitment
to it, so that we can look at my words in 5 years and say “boy, was
Emmanuele wrong”; or to look at alternatives, and explore new venues in
order to make <span class="caps">GNOME</span> (and the larger free software ecosystem) better.</p>Epoxy2017-02-11T01:34:00+00:002017-02-11T01:37:36+00:00ebassitag:www.bassi.io,2017-02-11:/articles/2017/02/11/epoxy/<p>In which I recount the process of moving libepoxy to Meson and becoming its maintainer</p><p><a href="https://github.com/anholt/libepoxy">Epoxy</a> is a small library that <span class="caps">GTK</span>+,
and other projects, use in order to access the OpenGL <span class="caps">API</span> in somewhat sane
fashion, hiding all the awful bits of craziness that actually need to happen
because apparently somebody dosed the water supply at <span class="caps">SGI</span> with large
quantities of <span class="caps">LSD</span> in the mid-‘90s, or something.</p>
<p>As an added advantage, Epoxy is also portable on different platforms, which
is a plus for <span class="caps">GTK</span>+.</p>
<p>Since I’ve started using <a href="http://mesonbuild.com">Meson</a> for my personal (and
some <a href="https://github.com/ebassi/emeus">work-related</a>) projects as well, I’ve
been on the lookout for adding Meson build rules to other free and open
source software projects, in order to improve both their build time and
portability, and to improve Meson itself.</p>
<p>As a small, portable project, Epoxy sounded like a good candidate for the
port of its build system from autotools to <a href="http://mesonbuild.com">Meson</a>.</p>
<p>To the Bat Build Machine!</p>
<h3>tl;dr</h3>
<p>Since you may be interested just in <a href="https://github.com/ebassi/libepoxy/wiki/Moving-Epoxy-to-Meson">the numbers</a>,
building Epoxy with Meson on my Kaby Lake four Core i7 and NMVe <span class="caps">SSD</span> takes about
45% less time than building it with autotools.</p>
<p>A fairly good fraction of the autotools time is spent going through the
autogen and configure phases, because they both aren’t parallelised, and
create a ton of shell invocations.</p>
<p>Conversely, Meson’s configuration phase is incredibly fast; the <strong>whole</strong>
Meson build of Epoxy fits in the same time the autogen.sh and configure
scripts complete their run.</p>
<h3>Administrivia</h3>
<p>Epoxy is a simple library, which means it does not need a hugely complicated
build system set up; it does have some interesting deviations, though, which
made the porting an interesting challenge.</p>
<p>For instance, on Linux and similar operating systems Epoxy uses <code>pkg-config</code>
to find things like the <span class="caps">EGL</span> availability and the X11 headers and libraries;
on Windows, though, it relies on finding the <code>opengl32</code> shared or static
library object itself. This means that we get something straightforward in
the former case, like:</p>
<div class="highlight"><pre><span></span><code><span class="c1"># Optional dependencies</span>
<span class="n">gl_dep</span> <span class="o">=</span> <span class="n">dependency</span><span class="p">(</span><span class="s1">'gl'</span><span class="p">,</span> <span class="n">required</span><span class="p">:</span> <span class="n">false</span><span class="p">)</span>
<span class="n">egl_dep</span> <span class="o">=</span> <span class="n">dependency</span><span class="p">(</span><span class="s1">'egl'</span><span class="p">,</span> <span class="n">required</span><span class="p">:</span> <span class="n">false</span><span class="p">)</span>
</code></pre></div>
<p>and something slightly less straightforward in the latter case:</p>
<div class="highlight"><pre><span></span><code><span class="k">if</span> <span class="n">host_system</span> <span class="o">==</span> <span class="s1">'windows'</span>
<span class="c1"># Required dependencies on Windows</span>
<span class="n">opengl32_dep</span> <span class="o">=</span> <span class="n">cc</span><span class="o">.</span><span class="n">find_library</span><span class="p">(</span><span class="s1">'opengl32'</span><span class="p">,</span> <span class="n">required</span><span class="p">:</span> <span class="n">true</span><span class="p">)</span>
<span class="n">gdi32_dep</span> <span class="o">=</span> <span class="n">cc</span><span class="o">.</span><span class="n">find_library</span><span class="p">(</span><span class="s1">'gdi32'</span><span class="p">,</span> <span class="n">required</span><span class="p">:</span> <span class="n">true</span><span class="p">)</span>
<span class="n">endif</span>
</code></pre></div>
<p>And, still, this is miles better than what you have to deal with when using autotools.</p>
<p>Let’s take a messy thing in autotools, like checking whether or not the
compiler supports a set of arguments; usually, this involves some m4 macro
that’s either part of <a href="https://www.gnu.org/software/autoconf-archive/">autoconf-archive</a>
or some additional repository, like the <a href="https://cgit.freedesktop.org/xorg/util/macros">xorg macros</a>.
Meson handles this in a much better way, out of the box:</p>
<div class="highlight"><pre><span></span><code><span class="c1"># Use different flags depending on the compiler</span>
<span class="k">if</span> <span class="n">cc</span><span class="o">.</span><span class="n">get_id</span><span class="p">()</span> <span class="o">==</span> <span class="s1">'msvc'</span>
<span class="n">test_cflags</span> <span class="o">=</span> <span class="p">[</span>
<span class="s1">'-W3'</span><span class="p">,</span>
<span class="o">...</span><span class="p">,</span>
<span class="p">]</span>
<span class="k">elif</span> <span class="n">cc</span><span class="o">.</span><span class="n">get_id</span><span class="p">()</span> <span class="o">==</span> <span class="s1">'gcc'</span>
<span class="n">test_cflags</span> <span class="o">=</span> <span class="p">[</span>
<span class="s1">'-Wpointer-arith'</span><span class="p">,</span>
<span class="o">...</span><span class="p">,</span>
<span class="p">]</span>
<span class="k">else</span>
<span class="n">test_cflags</span> <span class="o">=</span> <span class="p">[</span> <span class="p">]</span>
<span class="n">endif</span>
<span class="n">common_cflags</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">foreach</span> <span class="n">cflag</span><span class="p">:</span> <span class="n">test_cflags</span>
<span class="k">if</span> <span class="n">cc</span><span class="o">.</span><span class="n">has_argument</span><span class="p">(</span><span class="n">cflag</span><span class="p">)</span>
<span class="n">common_cflags</span> <span class="o">+=</span> <span class="p">[</span> <span class="n">cflag</span> <span class="p">]</span>
<span class="n">endif</span>
<span class="n">endforeach</span>
</code></pre></div>
<p>In terms of speed, the configuration step could be made even faster by
parallelising the compiler argument checks; right now, Meson has to do them
all in a series, but nothing except some additional parsing effort would
prevent Meson from running the whole set of checks in parallel, and gather
the results at the end.</p>
<h3>Generating code</h3>
<p>In order to use the <span class="caps">GL</span> entry points without linking against <code>libGL</code> or
<code>libGLES*</code> Epoxy takes the <span class="caps">XML</span> description of the <span class="caps">API</span> from the Khronos
repository and generates the code that ends up being compiled by using a
Python script to parse the <span class="caps">XML</span> and generating header and source files.</p>
<p>Additionally, and unlike most libraries in the G* stack, Epoxy stores its
public headers inside a separate directory from its sources:</p>
<div class="highlight"><pre><span></span><code><span class="n">libepoxy</span>
<span class="err">├──</span><span class="w"> </span><span class="n">cross</span>
<span class="err">├──</span><span class="w"> </span><span class="n">doc</span>
<span class="err">├──</span><span class="w"> </span><span class="n">include</span>
<span class="err">│</span><span class="w"> </span><span class="err">└──</span><span class="w"> </span><span class="n">epoxy</span>
<span class="err">├──</span><span class="w"> </span><span class="n">registry</span>
<span class="err">├──</span><span class="w"> </span><span class="n">src</span>
<span class="err">└──</span><span class="w"> </span><span class="n">test</span>
</code></pre></div>
<p>The autotools build has the <code>src/gen_dispatch.py</code> script create both the
source and the header file for each <span class="caps">XML</span> at the same time using a rule
processed when recursing inside the <code>src</code> directory, and proceeds to put the
generated header under <code>$(top_builddir)/include/epoxy</code>, and the generated
source under <code>$(top_builddir)/src</code>. Each code generation rule in the
<code>Makefile</code> manually creates the <code>include/epoxy</code> directory under the build
root to make up for parallel dispatch of each rule.</p>
<p>Meson makes is harder to do this kind of spooky-action-at-a-distance build,
so we need to generate the headers in one pass, and the source in another.
This is a bit of a let down, to be honest, and yet a build that invokes the
generator script twice for each <span class="caps">API</span> description file is still faster under
Ninja than a build with the single invocation under Make.</p>
<p>There are sill issues in this step that are being addressed by the Meson
developers; for instance, right now we have to use a custom target for each
generated header and source separately instead of declaring a generator and
calling it multiple times. Hopefully, this will be fixed fairly soon.</p>
<h3>Documentation</h3>
<p>Epoxy has a <strong>very</strong> small footprint, in terms of <span class="caps">API</span>, but it still benefits
from having some documentation on its use. I decided to generate the <span class="caps">API</span>
reference using <a href="http://www.stack.nl/~dimitri/doxygen/">Doxygen</a>, as it’s
not a G* library and does not need the additional features of gtk-doc.
Sadly, Doxygen’s default style is absolutely terrible; it would be great if
somebody could fix it to make it look half as good as the look gtk-doc gets
out of the box.</p>
<h3>Cross-compilation and native builds</h3>
<p>Now we get into “interesting” territory.</p>
<p>Epoxy is portable; it works on Linux and *<span class="caps">BSD</span> systems; on macOS; and on
Windows. Epoxy also works on both Intel Architecture and on <span class="caps">ARM</span>.</p>
<p>Making it run on Unix-like systems is not at all complicated. When it comes
to Windows, though, things get weird fast.</p>
<p>Meson uses <a href="https://github.com/mesonbuild/meson/wiki/Cross%20compilation">cross files</a>
to determine the environment and toolchain of the <em>host machine</em>, i.e. the
machine where the result of the build will eventually run. These are simple
text files with key/value pairs that you can either keep in a separate
repository, in case you want to share among projects; or you can keep them
in your own project’s repository, especially if you want to easily set up
continuous integration of cross-compilation builds.</p>
<p>Each toolchain has its own; for instance, this is the description of a cross
compilation done on Fedora with MingW:</p>
<div class="highlight"><pre><span></span><code><span class="k">[binaries]</span>
<span class="na">c</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">'/usr/bin/x86_64-w64-mingw32-gcc'</span>
<span class="na">cpp</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">'/usr/bin/x86_64-w64-mingw32-cpp'</span>
<span class="na">ar</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">'/usr/bin/x86_64-w64-mingw32-ar'</span>
<span class="na">strip</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">'/usr/bin/x86_64-w64-mingw32-strip'</span>
<span class="na">pkgconfig</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">'/usr/bin/x86_64-w64-mingw32-pkg-config'</span>
<span class="na">exe_wrapper</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">'wine'</span>
</code></pre></div>
<p>This section tells Meson where the binaries of the MingW toolchain are; the
<code>exe_wrapper</code> key is useful to run the tests under Wine, in this case.</p>
<p>The cross file also has an additional section for things like special
compiler and linker flags:</p>
<div class="highlight"><pre><span></span><code><span class="k">[properties]</span>
<span class="na">root</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">'/usr/x86_64-w64-mingw32/sys-root/mingw'</span>
<span class="na">c_args</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">[ '-pipe', '-Wp,-D_FORTIFY_SOURCE=2', '-fexceptions', '--param=ssp-buffer-size=4', '-I/usr/x86_64-w64-mingw32/sys-root/mingw/include' ]</span>
<span class="na">c_link_args</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">[ '-L/usr/x86_64-w64-mingw32/sys-root/mingw/lib' ]</span>
</code></pre></div>
<p>These values are taken from the equivalent bits that Fedora provides in
their MingW RPMs.</p>
<p>Luckily, the tool that generates the headers and source files is written in
Python, so we don’t need an additional layer of complexity, with a tool
built and run on a different platform and architecture in order to generate
files to be built and run on a different platform.</p>
<h3>Continuous Integration</h3>
<p>Of course, any decent process of porting, these days, should deal with
continuous integration. <span class="caps">CI</span> gives us confidence as to whether or not any
change whatsoever we make actually works — and not just on our own computer,
and our own environment.</p>
<p>Since Epoxy is hosted on GitHub, the quickest way to deal with continuous
integration is to use <a href="https://travis-ci.org">TravisCI</a>, for Linux and
macOS; and <a href="https://www.appveyor.com/">Appveyor</a> for Windows.</p>
<p>The requirements for Meson are just Python3 and Ninja; Epoxy also requires
Python 2.7, for the dispatch generation script, and the shared libraries for
<span class="caps">GL</span> and the native <span class="caps">API</span> needed to create a <span class="caps">GL</span> context (<span class="caps">GLX</span>, <span class="caps">EGL</span>, or <span class="caps">WGL</span>); it
also optionally needs the X11 libraries and headers and Xvfb for running the
test suite.</p>
<p>Since Travis offers an older version of Ubuntu <span class="caps">LTS</span> as its base system, we
cannot build Epoxy with Meson; additionally, running the test suite is a
crapshoot because the Mesa version if hopelessly out of date and will either
cause most of the tests to be skipped or, worse, make them segfault. To
sidestep this particular issue, I’ve prepared a <a href="https://github.com/ebassi/epoxyci">Docker image</a>
with its own harness, and I use it as the containerised environment for Travis.</p>
<p>On Appveyor, thanks to the contribution of <a href="https://github.com/tmarrinan">Thomas Marrinan</a>
we just need to download Python3, Python2, and Ninja, and build everything
inside its own root; as an added bonus, Appveyor allows us to take the build
artefacts when building from a tag, and shoving them into a zip file that
gets deployed to the release page on GitHub.</p>
<h3>Conclusion</h3>
<p>Most of this work has been done off and on over a couple of months; the
rough Meson build conversion was done last December, with the
cross-compilation and native builds taking up the last bit of work.</p>
<p>Since <a href="http://anholt.livejournal.com/">Eric</a> does not have any more spare
time to devote to Epoxy, he was kind enough to give me access to the
original repository, and I’ve tried to reduce the amount of open pull
requests and issues there.</p>
<p>I’ve also <a href="https://github.com/anholt/libepoxy/releases/tag/v1.4">released version 1.4.0</a>
and I plan to do a 1.4.1 release soon-ish, now that I’m positive Epoxy works
on Windows.</p>
<p>I’d like to thank:</p>
<ul>
<li>Eric Anholt, for writing Epoxy and helping out when I needed a hand
with it</li>
<li><a href="http://nibblestew.blogspot.co.uk/">Jussi Pakkanen</a> and
<a href="http://blog.nirbheek.in/search/label/gnome">Nirbheek Chauhan</a>, for writing
Meson and for helping me out with my dumb questions on <code>#mesonbuild</code></li>
<li>Thomas Marrinan, for working on the Appveyor integration and testing
Epoxy builds on Windows</li>
<li>Yaron Cohen-Tal, for maintaining Epoxy in the interim</li>
</ul>Constraints editing2017-01-11T14:30:00+00:002023-05-31T15:36:09+01:00ebassitag:www.bassi.io,2017-01-11:/articles/2017/01/11/constraints-editing/<p>In which a wild editor of constraints appears inside Emeus</p><p><a href="https://www.bassi.io/articles/2016/11/01/constraints-reprise/">Last year</a> I talked about the newly added support for Apple’s
Visual Format Language in <a href="https://ebassi.github.io/emeus/">Emeus</a>, which allows to quickly
describe layouts using a cross between <span class="caps">ASCII</span> art and predicates. For
instance, I can use:</p>
<div class="highlight"><pre><span></span><code><span class="nl">H</span><span class="p">:</span><span class="o">|-[</span><span class="n">icon(==256)</span><span class="o">]-[</span><span class="n">name_label</span><span class="o">]-|</span>
<span class="nl">H</span><span class="p">:</span><span class="o">[</span><span class="n">surname_label</span><span class="o">]-|</span>
<span class="nl">H</span><span class="p">:</span><span class="o">[</span><span class="n">email_label</span><span class="o">]-|</span>
<span class="nl">H</span><span class="p">:</span><span class="o">|-[</span><span class="n">button(<=icon)</span><span class="o">]</span>
<span class="nl">V</span><span class="p">:</span><span class="o">|-[</span><span class="n">icon(==256)</span><span class="o">]</span>
<span class="nl">V</span><span class="p">:</span><span class="o">|-[</span><span class="n">name_label</span><span class="o">]-[</span><span class="n">surname_label</span><span class="o">]-[</span><span class="n">email_label</span><span class="o">]-|</span>
<span class="nl">V</span><span class="p">:</span><span class="o">[</span><span class="n">button</span><span class="o">]-|</span>
</code></pre></div>
<p>and obtain a layout like this one:</p>
<p><figure>
<figcaption class="image-caption">
<p>Boxes approximate widgets</p>
</figcaption>
<div><img src="https://www.bassi.io/images/emeus-user-details.png"/></div>
</figure></p>
<p>Thanks to the contribution of my colleague Martin Abente Lahaye, now Emeus
supports extensions to the <span class="caps">VFL</span>, namely:</p>
<ul>
<li>arithmetic operators for constant and multiplication factors inside
predicates, like <code>[button1(button2 * 2 + 16)]</code></li>
<li>explicit attribute references, like <code>[button1(button1.height / 2)]</code></li>
</ul>
<p>This allows more expressive layout descriptions, like keeping aspect ratios
between <span class="caps">UI</span> elements, without requiring hitting the code base.</p>
<p>Of course, editing <span class="caps">VFL</span> descriptions blindly is not what I consider a fun
activity, so I took some time to write a simple, primitive editing tool that
lets you visualize a layout expressed through <span class="caps">VFL</span> constraints:</p>
<p><figure>
<figcaption class="image-caption">
<p>I warned you that it was primitive and simple</p>
</figcaption>
<div><img src="https://www.bassi.io/images/emeus-edit-constraints.png"/></div>
</figure></p>
<p>Here’s a couple of videos showing it in action:</p>
<p><span class="videobox">
<iframe width="640" height="480"
src='https://www.youtube.com/embed/wroDZQi7HWA'
frameborder='0' webkitAllowFullScreen
mozallowfullscreen allowFullScreen>
</iframe>
</span></p>
<p><span class="videobox">
<iframe width="640" height="480"
src='https://www.youtube.com/embed/_nTvuEx1Wvs'
frameborder='0' webkitAllowFullScreen
mozallowfullscreen allowFullScreen>
</iframe>
</span></p>
<p>At some point, this could lead to a new <span class="caps">UI</span> tool to lay out widgets inside
<a href="https://wiki.gnome.org/Apps/Builder">Builder</a> and/or Glade.</p>
<p>As of now, I consider Emeus in a stable enough state for other people to
experiment with it — I’ll probably make a release soon-ish. The Emeus
<a href="https://ebassi.github.io/emeus/">website</a> is up to date, as it is <a href="https://ebassi.github.io/emeus/docs/">the <span class="caps">API</span> reference</a>,
and I’m happy to review pull requests and feature requests.</p>Laptop review2016-12-17T00:00:00+00:002016-12-17T18:31:37+00:00ebassitag:www.bassi.io,2016-12-17:/articles/2016/12/17/laptop-review/<p>In which I do a review of two laptops: Dell <span class="caps">XPS</span> (2016) and Lenovo Yoga 900</p><h2>Dell <span class="caps">XPS</span> 13 (Developer Edition 2016)</h2>
<p>After three and a half years with my trusty mid-2013 MacBook Air, I decided
to get a new personal laptop. To be fair, my Air could have probably lasted
another 12-18 months, even though its <span class="caps">8GB</span> of <span class="caps">RAM</span> and Haswell Core i7 were
starting to get pretty old for system development. The reason why I couldn’t
keep using it reliably was that the <span class="caps">SSD</span> had already started showing <span class="caps">SMART</span>
errors in January, and I already had to reset it and re-install from scratch
once. Refurbishing the <span class="caps">SSD</span> out of warranty is still an option, if I decided
to fork over a fair chunk of money and could live without a laptop for about
a month<sup id="fnref:1"><a class="footnote-ref" href="#fn:1">1</a></sup>.</p>
<p>After getting recommendations for the previous <span class="caps">XPS</span> iterations by various
other free software developers and Linux users, I waited until the new, Kaby
Lake based model was available in the <span class="caps">EU</span> and ordered one. After struggling a
bit with Dell’s website, I managed to get an <span class="caps">XPS</span> 13 with a <span class="caps">US</span> keyboard
layout<sup id="fnref:2"><a class="footnote-ref" href="#fn:2">2</a></sup> — which took about two weeks from order to delivery.</p>
<p>The hardware out of the box experience is pretty neat, with a nice, clean
box; very Apple-like. The software’s first boot experience could be better,
to say the least. Since I chose the Developer Edition, I got Ubuntu as the
main <span class="caps">OS</span> instead of Windows, and I have been thoroughly underwhelmed by the
effort spent by Dell and Canonical in polishing the software side of things.
As soon as you boot the laptop, you’re greeted with an abstract video
playing while the system does <em>something</em>. The video playback is not
skippable, and does not have volume controls, so I got to “experience” it at
full blast out of the speakers.</p>
<p>Ubuntu’s first boot experience <span class="caps">UI</span> to configure the machine is rudimentary,
at best, and not really polished; it’s the installer <span class="caps">UI</span> without the actual
installation bits, but it clearly hasn’t been refined for the HiDPI screen.
The color scheme has progressively gone worse over the years; while all
other OSes are trying to convey a theme of lightness using soft tones, the
dark grey, purple, and dark orange tones used by Ubuntu make the whole <span class="caps">UI</span>
seem heavier and oppressive. </p>
<p>After that, you get into Unity, and no matter how many times I try it, I
still cannot enjoy using it. I also realized <em>why</em> various people coming
from Ubuntu complain about the <span class="caps">GNOME</span> theme being too heavy on the
whitespace: the Ubuntu default theme is super-compressed, with controls
hugging together so closely that they almost seem to overlap. There is
barely no affordance for the pointer, let alone for interacting through the touchscreen.</p>
<p>All in all, I resisted half a day on it, mostly to see what was the state of
stock Ubuntu after many years of Fedora<sup id="fnref:3"><a class="footnote-ref" href="#fn:3">3</a></sup>. After that, I downloaded a
Fedora 25 <span class="caps">USB</span> image and re-installed from scratch.</p>
<p>Sadly, I still have to report that Anaconda doesn’t shine at all. Luckily, I
didn’t have to deal with dual booting, so I only needed to interact with the
installer just enough to tell it to use the stock on disk layout and create
the <code>root</code> user. Nevertheless, figuring out how to tell it to split my
<code>/home</code> volume and encrypt it required me to go through the partitioning
step three times because I couldn’t for the life of me understand how to
commit to the layout I wanted.</p>
<p>After that, I was greeted by <span class="caps">GNOME</span>’s first boot experience — which is
definitely more polished than Ubuntu’s, but it’s still a bit too
“functional” and plain.</p>
<p>Fedora recognised the whole hardware platform out of the box: wifi,
bluetooth, webcam, HiDPI screen. On the power management side, I was able to
wring out about 8 hours of work (compilation, editing, web browsing, and a
couple of Google hangouts) while on wifi, without having to plug in the <span class="caps">AC</span>.</p>
<p>Coming from years of Apple laptops, I was especially skeptical of the
quality of the touchpad, but I have to say I was pleasantly surprised by its
accuracy and feedback. It’s not MacBook-level, but it’s definitely the
closest anyone has ever been to that slice of fried gold.</p>
<p>The only letdowns I can find are the position of the webcam, which is on the
bottom of the panel and to the left, which makes for <strong>very</strong> dramatic
angles when doing video calls, and requires you never type if you don’t want
your fingers to be in the way; and the power brick, which has its own
proprietary connector. There’s a <span class="caps">USB</span>-C port, though, so there may be
provisions for powering the laptop through it.</p>
<h3>The good</h3>
<ul>
<li>Fully supported hardware (Fedora 25)</li>
<li>Excellent battery life</li>
<li>Nice keyboard</li>
<li>Very good touchpad</li>
</ul>
<h3>The bad</h3>
<ul>
<li>The position of the webcam</li>
<li>Yet another power brick with custom connector I have to lug around</li>
</ul>
<hr>
<h2>Lenovo Yoga</h2>
<p>Thanks to my <a href="https://endlessm.com">employer</a> I now have a work laptop as
well, in the shape of a Lenovo Yoga 900. I honestly crossed off Lenovo as a
vendor after the vast <a href="https://www.eff.org/deeplinks/2015/02/further-evidence-lenovo-breaking-https-security-its-laptops">amounts</a> of <a href="http://www.zdnet.com/article/lenovo-begs-users-to-uninstall-accelerator-app-in-the-name-of-security/">stupidity</a>
they imposed on their clients — and that was after I decided to stop buying
ThinkPad-branded laptops, given their declining build quality and bad
technical choices. Nevertheless, you don’t look a gift horse in the mouth.</p>
<p>The out of the box experience of the Yoga is very much on par with the one I
had with the <span class="caps">XPS</span>, which is to say: fairly Apple-like.</p>
<p>The Yoga 900 is a fairly well made machine. It’s an Intel Sky Lake platform,
with a nice screen and good components. The screen can fold and turn the
whole thing into a “tablet”, except that the keyboard faces downward, so
it’s weird to handle in that mode. Plus, a 13” tablet is a pretty big thing
to carry around. On the other hand, folding the laptop into a “tent” and
using an external keyboard and pointer device is a nice twist on the whole
“home office” approach. The webcam is, thankfully, centered and placed at
the top of the panel — something that Lenovo has apparently changed in the
910 model, when they realised that folding the laptop would put the webcam
at the bottom of the panel.</p>
<p>On the software side, the first boot experience into Windows 10 was
definitely less than stellar. The Lenovo <span class="caps">FBE</span> software was <strong>not</strong>
HiDPI-aware, which posed interesting challenges to the user interaction.
This is something that a simple bit of <span class="caps">QA</span> would have found out, but
apparently <span class="caps">QA</span> is too much to ask when dealing with a £1000 laptop. Luckily,
I had to deal with that only inasmuch as I needed to get and install the
latest firmware updates before installing Linux on the machine. Again, I
went for Fedora.</p>
<p>As in the case of the Dell <span class="caps">XPS</span>, Fedora recognised all components of the
hardware plaform out of the box. Even the screen rotation and folding works
out of the box — though it can still get into inconsistent states when you
move the laptop around, so I kind of recommend you keep the screen rotation
locked until you actually need it.</p>
<p>On the power management side, I was impressed by how well the sleep states
conserve battery power; I’m able to leave the Yoga suspended for a week and
still have power on resume. The power brick has a weird <span class="caps">USB</span>-like connector
to the laptop which makes me wonder what on earth were Lenovo engineers
thinking; on the other hand, the adapter has a <span class="caps">USB</span> port which means you can
charge it from a battery pack or from a <span class="caps">USB</span> adapter as well. There’s also a
<span class="caps">USB</span>-C port, but I still haven’t tested if I can put power through it.</p>
<p>The keyboard is probably the biggest let down; the travel distance and feel
of the keys is definitely not up to par with the Dell <span class="caps">XPS</span>, or with the Apple
keyboards. The 900 has an additional column of navigation keys on the right
edge that invariably messes up my finger memory — though it seems that the
910 has moved them to Function key combinations.<sup id="fnref:5"><a class="footnote-ref" href="#fn:5">5</a></sup> The power button is on
the right side of the laptop, which makes for unintended suspend/resume
cycles when trying to plug in the headphones, or when moving the laptop. The
touchpad is, sadly, very much lacking, with ghost tap events that forced me
to disable the middle-click emulation everywhere<sup id="fnref:4"><a class="footnote-ref" href="#fn:4">4</a></sup>.</p>
<h3>The good</h3>
<ul>
<li>Fully supported hardware (Fedora 25)</li>
<li>Solid build</li>
<li>Nice flip action</li>
<li>Excellent power management</li>
</ul>
<h3>The bad</h3>
<ul>
<li>Keyboard is a toy</li>
<li>Touchpad is a pale imitation of a good pointing device</li>
</ul>
<div class="footnote">
<hr>
<ol>
<li id="fn:1">
<p>Which may still happen, all things considered; I really like the Air
as a travel laptop. <a class="footnote-backref" href="#fnref:1" title="Jump back to footnote 1 in the text">↩</a></p>
</li>
<li id="fn:2">
<p>After almost a decade with <span class="caps">US</span> layouts I find the <span class="caps">UK</span> layout inferior
to the point of inconvenience. <a class="footnote-backref" href="#fnref:2" title="Jump back to footnote 2 in the text">↩</a></p>
</li>
<li id="fn:3">
<p>On my desktop machine/gaming rig I dual boot between Windows 10 and
Ubuntu <span class="caps">GNOME</span>, mostly because of the nVidia <span class="caps">GPU</span> and Steam. <a class="footnote-backref" href="#fnref:3" title="Jump back to footnote 3 in the text">↩</a></p>
</li>
<li id="fn:4">
<p>That also increased my hatred of the middle-click-to-paste-selection
easter egg a thousandfold, and I already hated the damned thing so much
that my rage burned with the intensity of a million suns. <a class="footnote-backref" href="#fnref:4" title="Jump back to footnote 4 in the text">↩</a></p>
</li>
<li id="fn:5">
<p>Additionally, the keyboard layout is <span class="caps">UK</span> — see note 2 above. <a class="footnote-backref" href="#fnref:5" title="Jump back to footnote 5 in the text">↩</a></p>
</li>
</ol>
</div>Constraints (reprise)2016-11-01T17:07:00+00:002016-11-18T23:09:18+00:00ebassitag:www.bassi.io,2016-11-01:/articles/2016/11/01/constraints-reprise/<p>Further experiments with constraint-based layout systems for <span class="caps">GTK</span>+</p><p>After the <a href="https://www.bassi.io/articles/2016/10/17/constraints/">first article on Emeus</a> various people expressed
interest in the internals of the library, so I decided to talk a bit about
what makes it work.</p>
<p>Generally, you can think about constraints as linear equations:</p>
<div class="highlight"><pre><span></span><code><span class="n">view1</span><span class="o">.</span><span class="n">attr1</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">view2</span><span class="o">.</span><span class="n">attr2</span><span class="w"> </span><span class="err">×</span><span class="w"> </span><span class="n">multiplier</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">constant</span>
</code></pre></div>
<p>You take the the value of <code>attr2</code> on the widget <code>view2</code>, multiply it by a
<code>multiplier</code>, add a <code>constant</code>, and apply the value to the attribute <code>attr1</code>
on the widget <code>view1</code>. You don’t need <code>view2.attr2</code> either, for instance:</p>
<div class="highlight"><pre><span></span><code><span class="n">view1</span><span class="o">.</span><span class="n">attr1</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">constant</span>
</code></pre></div>
<p>is a perfectly valid constraint.</p>
<p>You also don’t need to use an equality; these two constraints:</p>
<div class="highlight"><pre><span></span><code>view1.width ≥ 180
view1.width ≤ 250
</code></pre></div>
<p>specify that the <code>width</code> of <code>view1</code> must be in the <code>[ 180, 250 ]</code> range,
extremes included.</p>
<h3>Layout</h3>
<p>A layout, then, is just a pile of linear equations that describe the
relations between each element. So, if we have a simple grid:</p>
<div class="highlight"><pre><span></span><code><span class="nb">+--------------------------------------------+</span>
<span class="c">| super |</span>
<span class="c">| </span><span class="nb">+----------------+</span><span class="c"> </span><span class="nb">+-----------------+</span><span class="c"> |</span>
<span class="c">| | child1 | | child2 | |</span>
<span class="c">| | | | | |</span>
<span class="c">| </span><span class="nb">+----------------+</span><span class="c"> </span><span class="nb">+-----------------+</span><span class="c"> |</span>
<span class="c">| |</span>
<span class="c">| </span><span class="nb">+--------------------------------------+</span><span class="c"> |</span>
<span class="c">| | child3 | |</span>
<span class="c">| | | |</span>
<span class="c">| </span><span class="nb">+--------------------------------------+</span><span class="c"> |</span>
<span class="c">| |</span>
<span class="nb">+--------------------------------------------+</span>
</code></pre></div>
<p>We can describe each edge’s position and size using constraints. It’s
important to note that there’s an implicit “reading” order that makes it
easier to write constraints; in this case, we start from left to right, and
from top to bottom. Generally speaking, it’s possible to describe
constraints in any order, but the <a href="http://constraints.cs.washington.edu/cassowary/">Cassowary</a> solving
algorithm is geared towards the “reading” order above.</p>
<p>Each layout has some implicit constraint already available. For instance,
the “trailing” edge is equal to the leading edge plus the width; the bottom
edge is equal to the top edge plus the height; the center point is equal to
the width or height, divided by two, plus the leading or bottom edges. These
constraints help solving the layout, as well as provide additional values to
other constraints.</p>
<p>So, let’s start.</p>
<p>From the first row:</p>
<ul>
<li>the leading edge of the <code>super</code> container is the same as the leading
edge of <code>child1</code>, minus a padding</li>
<li>the trailing edge of <code>child1</code> is the same as the leading edge of
<code>child2</code>, minus a padding</li>
<li>the trailing edge of <code>child2</code> is the same as the trailing edge of
the <code>super</code> container, minus a padding</li>
<li>the width of <code>child1</code> is the same as the width of <code>child2</code></li>
</ul>
<p>From the second row:</p>
<ul>
<li>the leading edge of the <code>super</code> container is the same as the leading
edge of <code>child3</code>, minus a padding</li>
<li>the trailing edge of <code>child2</code> is the same as the trailing edge of
the <code>super</code> container, minus a padding</li>
</ul>
<p>From the first column:</p>
<ul>
<li>the top edge of the <code>super</code> container is the same as the top edge
of <code>child1</code>, minus a padding</li>
<li>the bottom edge of <code>child1</code> is the same as the top edge of <code>child3</code>,
minus a padding</li>
<li>the bottom edge of the <code>super</code> container is the same as the bottom
edge of <code>child3</code>, minus a padding</li>
<li>the height of <code>child3</code> is the same as the height of <code>child1</code></li>
</ul>
<p>From the second column:</p>
<ul>
<li>the top edge of the <code>super</code> container is the same as the top edge
of the <code>child2</code>, minus a padding</li>
<li>the bottom edge of <code>child1</code> is the same as the top edge of <code>child3</code>,
minus a padding</li>
<li>the bottom edge of the <code>super</code> container is the same as the bottom
edge of <code>child3</code>, minus a padding</li>
<li>the height of <code>child3</code> is the same as the height of <code>child2</code></li>
</ul>
<p>As you can see, there are some redundancies; these are necessary to ensure
that the layout is fully resolved, though obviously there are some
properties of the elements of the layout that implicitly eliminate some
results. For instance, if <code>child3</code><span class="quo">‘</span>s height is the same as <code>child1</code>, and
<code>child1</code> lies on the same row as <code>child2</code> and it’s an axis-aligned
rectangle, the it immediately follows that <code>child3</code> must have the same
height of <code>child2</code> as well. It’s important to note that, from a solver
perspective, there only are values, not boxes, and you could use the solver
with any kind of geometric shape; only the constraints give us the
information on what those shapes should be. It’s also easier to start from a
fully constrained layout and then remove constraints, than to start from a
loosely constrained layout and add constraints until it’s stable.</p>
<h3>Representation</h3>
<p>From the text description we can now get into a system of equations:</p>
<ul>
<li>super.start = child1.start - padding</li>
<li>child1.end = child2.start - padding</li>
<li>super.end = child2.end - padding</li>
<li>child1.width = child2.width</li>
<li>super.start = child3.start - padding</li>
<li>super.end = child3.end - padding</li>
<li>super.top = child1.top - padding</li>
<li>child1.bottom = child3.top - padding</li>
<li>super.bottom = child3.bottom - padding</li>
<li>child3.height = child1.height</li>
<li>super.top = child2.top - padding</li>
<li>child2.bottom = child3.top - padding</li>
<li>child3.height = child2.height</li>
</ul>
<p>Apple, in its <a href="https://en.wikipedia.org/wiki/Apple_Newton">infinite wisdom and foresight</a>, decided that
this form is still too verbose. After looking at the <a href="http://perldoc.perl.org/perlform.html">Perl format</a>
page for far too long, Apple engineers came up with the Visual Format
Language, or <a href="https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/AutolayoutPG/VisualFormatLanguage.html"><span class="caps">VFL</span></a> for short.</p>
<p>Using <span class="caps">VFL</span>, the constraints above become:</p>
<div class="highlight"><pre><span></span><code><span class="nl">H</span><span class="p">:</span><span class="o">|-</span><span class="p">(</span><span class="n">padding</span><span class="p">)</span><span class="o">-[</span><span class="n">child1(==child2)</span><span class="o">]-</span><span class="p">(</span><span class="n">padding</span><span class="p">)</span><span class="o">-[</span><span class="n">child2</span><span class="o">]-</span><span class="p">(</span><span class="n">padding</span><span class="p">)</span><span class="o">-|</span>
<span class="nl">H</span><span class="p">:</span><span class="o">|-</span><span class="p">(</span><span class="n">padding</span><span class="p">)</span><span class="o">-[</span><span class="n">child3</span><span class="o">]-</span><span class="p">(</span><span class="n">padding</span><span class="p">)</span><span class="o">-|</span>
<span class="nl">V</span><span class="p">:</span><span class="o">|-</span><span class="p">(</span><span class="n">padding</span><span class="p">)</span><span class="o">-[</span><span class="n">child1(==child3)</span><span class="o">]-</span><span class="p">(</span><span class="n">padding</span><span class="p">)</span><span class="o">-[</span><span class="n">child3</span><span class="o">]-</span><span class="p">(</span><span class="n">padding</span><span class="p">)</span><span class="o">-|</span>
<span class="nl">V</span><span class="p">:</span><span class="o">|-</span><span class="p">(</span><span class="n">padding</span><span class="p">)</span><span class="o">-[</span><span class="n">child2(==child3)</span><span class="o">]-</span><span class="p">(</span><span class="n">padding</span><span class="p">)</span><span class="o">-[</span><span class="n">child3</span><span class="o">]-</span><span class="p">(</span><span class="n">padding</span><span class="p">)</span><span class="o">-|</span>
</code></pre></div>
<p>Emeus, incidentally, ships with a simple utility that can take a set of <span class="caps">VFL</span>
format strings and generate GtkBuilder descriptions that you can embed into
your templates.</p>
<h3>Change</h3>
<p>We’ve used a fair amount of constraints, or four lines of faily cryptic
<span class="caps">ASCII</span> art, to basically describe a non-generic <code>GtkGrid</code> with two equally
sized horizontal cells on the first row, and a single cell with a column
span of two; compared to the common layout managers inside <span class="caps">GTK</span>+, this does
not seem like a great trade off.</p>
<p>Except that we can describe any other layout without necessarily having to
pack widgets inside boxes, with margins and spacing and alignment rules; we
also don’t have to change the <em>hierarchy</em> of the boxes if we want to change
the layout. For instance, let’s say that we want <code>child3</code> to have a
different horizontal padding, and a minimum and maximum width; we just need
to change the constraints involved in that row:</p>
<div class="highlight"><pre><span></span><code>H:|-(hpadding)-[child3(>=250,<=500)]-(hpadding)-|
</code></pre></div>
<p>Additionally, we now want to decouple <code>child1</code> and <code>child3</code> heights, and
make <code>child1</code> a fixed height item:</p>
<div class="highlight"><pre><span></span><code><span class="nl">V</span><span class="p">:</span><span class="o">|-</span><span class="p">(</span><span class="n">padding</span><span class="p">)</span><span class="o">-[</span><span class="n">child1(==250)</span><span class="o">]-</span><span class="p">(</span><span class="n">padding</span><span class="p">)</span><span class="o">-[</span><span class="n">child3</span><span class="o">]-</span><span class="p">(</span><span class="n">padding</span><span class="p">)</span><span class="o">-|</span>
</code></pre></div>
<p>And make the height of <code>child3</code> move within a range of values:</p>
<div class="highlight"><pre><span></span><code><span class="nl">V</span><span class="p">:</span><span class="o">|-</span><span class="p">(</span><span class="n">padding</span><span class="p">)</span><span class="o">-[</span><span class="n">child2</span><span class="o">]-</span><span class="p">(</span><span class="n">padding</span><span class="p">)</span><span class="o">-[</span><span class="n">child3(>=200,<=300)</span><span class="o">]-</span><span class="p">(</span><span class="n">padding</span><span class="p">)</span><span class="o">-|</span>
</code></pre></div>
<p>For all these cases we’d have to add intermediate boxes in between our
children and the parent container — with all the issues of theming and
updating things like GtkBuilder <span class="caps">XML</span> descriptions that come with that.</p>
<h3>Future</h3>
<p>The truth is, though, that describing layouts in terms of constraints is
another case of software engineering your way out of talking with
designers; it’s great to start talking about incremental simplex solvers,
and systems of linear equations, and <span class="caps">ASCII</span> art to describe your layouts, but
it doesn’t make <span class="caps">UI</span> designers <em>really</em> happy. They can deal with it, and
having a declarative language to describe constraints is more helpful than
parachuting them into an <span class="caps">IDE</span> with a Swiss army knife and a can of beans, but
I wouldn’t recommend it as a solid approach to developer experience.</p>
<p>Havoc wrote <a href="https://blog.ometer.com/2016/09/17/layout-apis-dont-have-to-be-terrible-lessons-from-bokeh/">a great article</a> on how layout management
<span class="caps">API</span> doesn’t necessarily have to suck:</p>
<ul>
<li>we can come up with a better, descriptive <span class="caps">API</span> that does not make
engineers and designers cringe in different ways</li>
<li>we should have support from our tools, in order to manipulate constraints
and <span class="caps">UI</span> elements</li>
<li>we should be able to combine boxes (which are easy to style) and
constraints (which are easy to lay out) together in a natural and
flexible way</li>
</ul>
<p>Improving layout management should be a goal in the development of <span class="caps">GTK</span>+ 4.0,
so feel free to jump in and help out.</p>Constraints2016-10-17T18:30:00+01:002023-02-20T00:37:38+00:00ebassitag:www.bassi.io,2016-10-17:/articles/2016/10/17/constraints/<p>Experiments with constraint-based layout systems for <span class="caps">GTK</span>+</p><p><span class="caps">GUI</span> toolkits have different ways to lay out the elements that compose an
application’s <span class="caps">UI</span>. You can go from the fixed layout management — somewhat
best represented by the old ‘90s Visual tools from Microsoft; to the
“springs and struts” model employed by the Apple toolkits until recently; to
the “boxes inside boxes inside boxes” model that <span class="caps">GTK</span>+ uses to this day. All
of these layout policies have their own distinct pros and cons, and it’s not
unreasonable to find that many toolkits provide support for more than one
policy, in order to cater to more use cases.</p>
<p>For instance, while <span class="caps">GTK</span>+ user interfaces are mostly built using nested boxes
to control margins, spacing, and alignment of widgets, there’s a sizeable
portion of <span class="caps">GTK</span>+ developers that end up using <a href="https://developer.gnome.org/gtk3/stable/GtkFixed.html">GtkFixed</a> or
<a href="https://developer.gnome.org/gtk3/stable/GtkLayout.html">GtkLayout</a> containers because they need fixed positioning
of children widget — until they regret it, because now they have to handle
things like reflowing, flipping contents in right-to-left locales, or font
size changes.</p>
<p>Additionally, most <span class="caps">UI</span> designers do not tend to “think with boxes”, unless
it’s for Web pages, and even in that case <span class="caps">CSS</span> affords a certain freedom that
cannot be replicated in a <span class="caps">GUI</span> toolkit. This usually results in engineers
translating a <span class="caps">UI</span> specification made of ties and relations between <span class="caps">UI</span>
elements into something that can be expressed with a pile of grids, boxes,
bins, and stacks — with all the back and forth, validation, and resources
that the translation entails.</p>
<p>It would certainly be easier if we could express a <span class="caps">GUI</span> layout in the same
set of relationships that can be traced on a piece of paper, a <span class="caps">UI</span> design
tool, or a design document:</p>
<ul>
<li>this label is at 8px from the leading edge of the box</li>
<li>this entry is on the same horizontal line as the label, its leading
edge at 12px from the trailing edge of the label</li>
<li>the entry has a minimum size of 250px, but can grow to fill the
available space</li>
<li>there’s a 90px button that sits between the trailing edge of the
entry and the trailing edge of the box, with 8px between either
edges and itself</li>
</ul>
<p>Sure, all of these constraints can be replaced by a couple of boxes; some
packing properties; margins; and minimum preferred sizes. If the design
changes, though, like it often does, reconstructing the <span class="caps">UI</span> can become
arbitrarily hard. This, in turn, leads to pushback to design changes from
engineers — and the cost of iterating over a <span class="caps">GUI</span> is compounded by technical inertia.</p>
<p>For my daily work at Endless I’ve been interacting with our design team for
a while, and trying to get from design specs to applications more quickly,
and with less inertia. Having <span class="caps">CSS</span> available allowed designers to be more
involved in the iterative development process, but the <span class="caps">CSS</span> subset that <span class="caps">GTK</span>+
implements is not allowed — for eminently good reasons — to change the <span class="caps">UI</span>
layout. We could go “full Web”, but that comes with a very large set of
drawbacks — performance on low end desktop devices, distribution,
interaction with system services being just the most glaring ones. A native
toolkit is still the preferred target for our platform, so I started looking
at ways to improve the lives of <span class="caps">UI</span> designers with the tools at our disposal.</p>
<p>Expressing layout through easier to understand relationships between its
parts is not a new problem, and as such it does not have new solutions;
other platforms, like the <a href="https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/AutolayoutPG/">Apple operating systems</a>, or
Google’s <a href="https://developer.android.com/training/constraint-layout/index.html">Android</a>, have started to provide this kind
of functionality — mostly available through their own <span class="caps">IDE</span> and <span class="caps">UI</span> building
tools, but also available programmatically. It’s even available for
platforms like <a href="http://ijzerenhein.github.io/autolayout.js/">the Web</a>.</p>
<p>What many of <a href="http://overconstrained.io">these solutions</a> seem to have in common
is using more or less the same solving algorithm — <a href="http://constraints.cs.washington.edu/cassowary/">Cassowary</a>.</p>
<p>Cassowary is:</p>
<blockquote>
<p>an incremental constraint solving toolkit that efficiently solves systems
of linear equalities and inequalities. Constraints may be either
requirements or preferences. Client code specifies the constraints to be
maintained, and the solver updates the constrained variables to have
values that satisfy the constraints.</p>
</blockquote>
<p>This makes it particularly suited for user interfaces.</p>
<p>The original implementation of Cassowary was written in 1998, in Java, C++,
and Smalltalk; since then, various other re-implementations surfaced:
Python, JavaScript, Haskell, slightly-more-modern-C++, etc.</p>
<aside>To continue in the naming policy of Cassowary implementations, this
small library is named after yet another flightless bird</aside>
<p>To that collection, I’ve now added my own — written in C/GObject — called
<a href="https://github.com/ebassi/emeus">Emeus</a>, which provides a <span class="caps">GTK</span>+ container and layout manager that
uses the Cassowary constraint solving algorithm to compute the allocation
of each child.</p>
<p>In spirit, the implementation is pretty simple: you create a new
<code>EmeusConstraintLayout</code> widget instance, add a bunch of widgets to it, and
then use <code>EmeusConstraint</code> objects to determine the relations between
children of the layout:</p>
<figure class='code'>
<figcaption><span class="liquid-tags-code-filename">simple-grid.js</span><span class="liquid-tags-code-lines">[Lines 89-170]</span><a href='/code/emeus/simple-grid.js'>download</a></figcaption>
<div class="highlight"><pre><span></span><code><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="nx">button1</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nx">Gtk</span><span class="p">.</span><span class="nx">Button</span><span class="p">({</span><span class="w"> </span><span class="nx">label</span><span class="o">:</span><span class="w"> </span><span class="s1">'Child 1'</span><span class="w"> </span><span class="p">});</span>
<span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">_layout</span><span class="p">.</span><span class="nx">pack</span><span class="p">(</span><span class="nx">button1</span><span class="p">,</span><span class="w"> </span><span class="s1">'child1'</span><span class="p">);</span>
<span class="w"> </span><span class="nx">button1</span><span class="p">.</span><span class="nx">show</span><span class="p">();</span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="nx">button2</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nx">Gtk</span><span class="p">.</span><span class="nx">Button</span><span class="p">({</span><span class="w"> </span><span class="nx">label</span><span class="o">:</span><span class="w"> </span><span class="s1">'Child 2'</span><span class="w"> </span><span class="p">});</span>
<span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">_layout</span><span class="p">.</span><span class="nx">pack</span><span class="p">(</span><span class="nx">button2</span><span class="p">,</span><span class="w"> </span><span class="s1">'child2'</span><span class="p">);</span>
<span class="w"> </span><span class="nx">button2</span><span class="p">.</span><span class="nx">show</span><span class="p">();</span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="nx">button3</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nx">Gtk</span><span class="p">.</span><span class="nx">Button</span><span class="p">({</span><span class="w"> </span><span class="nx">label</span><span class="o">:</span><span class="w"> </span><span class="s1">'Child 3'</span><span class="w"> </span><span class="p">});</span>
<span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">_layout</span><span class="p">.</span><span class="nx">pack</span><span class="p">(</span><span class="nx">button3</span><span class="p">,</span><span class="w"> </span><span class="s1">'child3'</span><span class="p">);</span>
<span class="w"> </span><span class="nx">button3</span><span class="p">.</span><span class="nx">show</span><span class="p">();</span>
<span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="nx">_layout</span><span class="p">.</span><span class="nx">add_constraints</span><span class="p">([</span>
<span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">Constraint</span><span class="p">({</span><span class="w"> </span><span class="nx">target_attribute</span><span class="o">:</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">ConstraintAttribute</span><span class="p">.</span><span class="nx">START</span><span class="p">,</span>
<span class="w"> </span><span class="nx">relation</span><span class="o">:</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">ConstraintRelation</span><span class="p">.</span><span class="nx">EQ</span><span class="p">,</span>
<span class="w"> </span><span class="nx">source_object</span><span class="o">:</span><span class="w"> </span><span class="nx">button1</span><span class="p">,</span>
<span class="w"> </span><span class="nx">source_attribute</span><span class="o">:</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">ConstraintAttribute</span><span class="p">.</span><span class="nx">START</span><span class="p">,</span>
<span class="w"> </span><span class="nx">constant</span><span class="o">:</span><span class="w"> </span><span class="o">-</span><span class="mf">8.0</span><span class="w"> </span><span class="p">}),</span>
<span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">Constraint</span><span class="p">({</span><span class="w"> </span><span class="nx">target_object</span><span class="o">:</span><span class="w"> </span><span class="nx">button1</span><span class="p">,</span>
<span class="w"> </span><span class="nx">target_attribute</span><span class="o">:</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">ConstraintAttribute</span><span class="p">.</span><span class="nx">WIDTH</span><span class="p">,</span>
<span class="w"> </span><span class="nx">relation</span><span class="o">:</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">ConstraintRelation</span><span class="p">.</span><span class="nx">EQ</span><span class="p">,</span>
<span class="w"> </span><span class="nx">source_object</span><span class="o">:</span><span class="w"> </span><span class="nx">button2</span><span class="p">,</span>
<span class="w"> </span><span class="nx">source_attribute</span><span class="o">:</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">ConstraintAttribute</span><span class="p">.</span><span class="nx">WIDTH</span><span class="w"> </span><span class="p">}),</span>
<span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">Constraint</span><span class="p">({</span><span class="w"> </span><span class="nx">target_object</span><span class="o">:</span><span class="w"> </span><span class="nx">button1</span><span class="p">,</span>
<span class="w"> </span><span class="nx">target_attribute</span><span class="o">:</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">ConstraintAttribute</span><span class="p">.</span><span class="nx">END</span><span class="p">,</span>
<span class="w"> </span><span class="nx">relation</span><span class="o">:</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">ConstraintRelation</span><span class="p">.</span><span class="nx">EQ</span><span class="p">,</span>
<span class="w"> </span><span class="nx">source_object</span><span class="o">:</span><span class="w"> </span><span class="nx">button2</span><span class="p">,</span>
<span class="w"> </span><span class="nx">source_attribute</span><span class="o">:</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">ConstraintAttribute</span><span class="p">.</span><span class="nx">START</span><span class="p">,</span>
<span class="w"> </span><span class="nx">constant</span><span class="o">:</span><span class="w"> </span><span class="o">-</span><span class="mf">12.0</span><span class="w"> </span><span class="p">}),</span>
<span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">Constraint</span><span class="p">({</span><span class="w"> </span><span class="nx">target_object</span><span class="o">:</span><span class="w"> </span><span class="nx">button2</span><span class="p">,</span>
<span class="w"> </span><span class="nx">target_attribute</span><span class="o">:</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">ConstraintAttribute</span><span class="p">.</span><span class="nx">END</span><span class="p">,</span>
<span class="w"> </span><span class="nx">relation</span><span class="o">:</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">ConstraintRelation</span><span class="p">.</span><span class="nx">EQ</span><span class="p">,</span>
<span class="w"> </span><span class="nx">source_attribute</span><span class="o">:</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">ConstraintAttribute</span><span class="p">.</span><span class="nx">END</span><span class="p">,</span>
<span class="w"> </span><span class="nx">constant</span><span class="o">:</span><span class="w"> </span><span class="o">-</span><span class="mf">8.0</span><span class="w"> </span><span class="p">}),</span>
<span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">Constraint</span><span class="p">({</span><span class="w"> </span><span class="nx">target_attribute</span><span class="o">:</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">ConstraintAttribute</span><span class="p">.</span><span class="nx">START</span><span class="p">,</span>
<span class="w"> </span><span class="nx">relation</span><span class="o">:</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">ConstraintRelation</span><span class="p">.</span><span class="nx">EQ</span><span class="p">,</span>
<span class="w"> </span><span class="nx">source_object</span><span class="o">:</span><span class="w"> </span><span class="nx">button3</span><span class="p">,</span>
<span class="w"> </span><span class="nx">source_attribute</span><span class="o">:</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">ConstraintAttribute</span><span class="p">.</span><span class="nx">START</span><span class="p">,</span>
<span class="w"> </span><span class="nx">constant</span><span class="o">:</span><span class="w"> </span><span class="o">-</span><span class="mf">8.0</span><span class="w"> </span><span class="p">}),</span>
<span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">Constraint</span><span class="p">({</span><span class="w"> </span><span class="nx">target_object</span><span class="o">:</span><span class="w"> </span><span class="nx">button3</span><span class="p">,</span>
<span class="w"> </span><span class="nx">target_attribute</span><span class="o">:</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">ConstraintAttribute</span><span class="p">.</span><span class="nx">END</span><span class="p">,</span>
<span class="w"> </span><span class="nx">relation</span><span class="o">:</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">ConstraintRelation</span><span class="p">.</span><span class="nx">EQ</span><span class="p">,</span>
<span class="w"> </span><span class="nx">source_attribute</span><span class="o">:</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">ConstraintAttribute</span><span class="p">.</span><span class="nx">END</span><span class="p">,</span>
<span class="w"> </span><span class="nx">constant</span><span class="o">:</span><span class="w"> </span><span class="o">-</span><span class="mf">8.0</span><span class="w"> </span><span class="p">}),</span>
<span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">Constraint</span><span class="p">({</span><span class="w"> </span><span class="nx">target_attribute</span><span class="o">:</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">ConstraintAttribute</span><span class="p">.</span><span class="nx">TOP</span><span class="p">,</span>
<span class="w"> </span><span class="nx">relation</span><span class="o">:</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">ConstraintRelation</span><span class="p">.</span><span class="nx">EQ</span><span class="p">,</span>
<span class="w"> </span><span class="nx">source_object</span><span class="o">:</span><span class="w"> </span><span class="nx">button1</span><span class="p">,</span>
<span class="w"> </span><span class="nx">source_attribute</span><span class="o">:</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">ConstraintAttribute</span><span class="p">.</span><span class="nx">TOP</span><span class="p">,</span>
<span class="w"> </span><span class="nx">constant</span><span class="o">:</span><span class="w"> </span><span class="o">-</span><span class="mf">8.0</span><span class="w"> </span><span class="p">}),</span>
<span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">Constraint</span><span class="p">({</span><span class="w"> </span><span class="nx">target_attribute</span><span class="o">:</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">ConstraintAttribute</span><span class="p">.</span><span class="nx">TOP</span><span class="p">,</span>
<span class="w"> </span><span class="nx">relation</span><span class="o">:</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">ConstraintRelation</span><span class="p">.</span><span class="nx">EQ</span><span class="p">,</span>
<span class="w"> </span><span class="nx">source_object</span><span class="o">:</span><span class="w"> </span><span class="nx">button2</span><span class="p">,</span>
<span class="w"> </span><span class="nx">source_attribute</span><span class="o">:</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">ConstraintAttribute</span><span class="p">.</span><span class="nx">TOP</span><span class="p">,</span>
<span class="w"> </span><span class="nx">constant</span><span class="o">:</span><span class="w"> </span><span class="o">-</span><span class="mf">8.0</span><span class="w"> </span><span class="p">}),</span>
<span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">Constraint</span><span class="p">({</span><span class="w"> </span><span class="nx">target_object</span><span class="o">:</span><span class="w"> </span><span class="nx">button1</span><span class="p">,</span>
<span class="w"> </span><span class="nx">target_attribute</span><span class="o">:</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">ConstraintAttribute</span><span class="p">.</span><span class="nx">BOTTOM</span><span class="p">,</span>
<span class="w"> </span><span class="nx">relation</span><span class="o">:</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">ConstraintRelation</span><span class="p">.</span><span class="nx">EQ</span><span class="p">,</span>
<span class="w"> </span><span class="nx">source_object</span><span class="o">:</span><span class="w"> </span><span class="nx">button3</span><span class="p">,</span>
<span class="w"> </span><span class="nx">source_attribute</span><span class="o">:</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">ConstraintAttribute</span><span class="p">.</span><span class="nx">TOP</span><span class="p">,</span>
<span class="w"> </span><span class="nx">constant</span><span class="o">:</span><span class="w"> </span><span class="o">-</span><span class="mf">12.0</span><span class="w"> </span><span class="p">}),</span>
<span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">Constraint</span><span class="p">({</span><span class="w"> </span><span class="nx">target_object</span><span class="o">:</span><span class="w"> </span><span class="nx">button2</span><span class="p">,</span>
<span class="w"> </span><span class="nx">target_attribute</span><span class="o">:</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">ConstraintAttribute</span><span class="p">.</span><span class="nx">BOTTOM</span><span class="p">,</span>
<span class="w"> </span><span class="nx">relation</span><span class="o">:</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">ConstraintRelation</span><span class="p">.</span><span class="nx">EQ</span><span class="p">,</span>
<span class="w"> </span><span class="nx">source_object</span><span class="o">:</span><span class="w"> </span><span class="nx">button3</span><span class="p">,</span>
<span class="w"> </span><span class="nx">source_attribute</span><span class="o">:</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">ConstraintAttribute</span><span class="p">.</span><span class="nx">TOP</span><span class="p">,</span>
<span class="w"> </span><span class="nx">constant</span><span class="o">:</span><span class="w"> </span><span class="o">-</span><span class="mf">12.0</span><span class="w"> </span><span class="p">}),</span>
<span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">Constraint</span><span class="p">({</span><span class="w"> </span><span class="nx">target_object</span><span class="o">:</span><span class="w"> </span><span class="nx">button3</span><span class="p">,</span>
<span class="w"> </span><span class="nx">target_attribute</span><span class="o">:</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">ConstraintAttribute</span><span class="p">.</span><span class="nx">HEIGHT</span><span class="p">,</span>
<span class="w"> </span><span class="nx">relation</span><span class="o">:</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">ConstraintRelation</span><span class="p">.</span><span class="nx">EQ</span><span class="p">,</span>
<span class="w"> </span><span class="nx">source_object</span><span class="o">:</span><span class="w"> </span><span class="nx">button1</span><span class="p">,</span>
<span class="w"> </span><span class="nx">source_attribute</span><span class="o">:</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">ConstraintAttribute</span><span class="p">.</span><span class="nx">HEIGHT</span><span class="w"> </span><span class="p">}),</span>
<span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">Constraint</span><span class="p">({</span><span class="w"> </span><span class="nx">target_object</span><span class="o">:</span><span class="w"> </span><span class="nx">button3</span><span class="p">,</span>
<span class="w"> </span><span class="nx">target_attribute</span><span class="o">:</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">ConstraintAttribute</span><span class="p">.</span><span class="nx">HEIGHT</span><span class="p">,</span>
<span class="w"> </span><span class="nx">relation</span><span class="o">:</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">ConstraintRelation</span><span class="p">.</span><span class="nx">EQ</span><span class="p">,</span>
<span class="w"> </span><span class="nx">source_object</span><span class="o">:</span><span class="w"> </span><span class="nx">button2</span><span class="p">,</span>
<span class="w"> </span><span class="nx">source_attribute</span><span class="o">:</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">ConstraintAttribute</span><span class="p">.</span><span class="nx">HEIGHT</span><span class="w"> </span><span class="p">}),</span>
<span class="w"> </span><span class="ow">new</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">Constraint</span><span class="p">({</span><span class="w"> </span><span class="nx">target_object</span><span class="o">:</span><span class="w"> </span><span class="nx">button3</span><span class="p">,</span>
<span class="w"> </span><span class="nx">target_attribute</span><span class="o">:</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">ConstraintAttribute</span><span class="p">.</span><span class="nx">BOTTOM</span><span class="p">,</span>
<span class="w"> </span><span class="nx">relation</span><span class="o">:</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">ConstraintRelation</span><span class="p">.</span><span class="nx">EQ</span><span class="p">,</span>
<span class="w"> </span><span class="nx">source_attribute</span><span class="o">:</span><span class="w"> </span><span class="nx">Emeus</span><span class="p">.</span><span class="nx">ConstraintAttribute</span><span class="p">.</span><span class="nx">BOTTOM</span><span class="p">,</span>
<span class="w"> </span><span class="nx">constant</span><span class="o">:</span><span class="w"> </span><span class="o">-</span><span class="mf">8.0</span><span class="w"> </span><span class="p">}),</span>
<span class="w"> </span><span class="p">]);</span>
</code></pre></div>
</figure>
<p><figure>
<figcaption class="image-caption">
<p>A simple grid</p>
</figcaption>
<div><img src="https://www.bassi.io/images/emeus-simple-grid.png"/></div>
</figure></p>
<p>This obviously looks like a ton of code, which is why I added the ability to
describe constraints inside GtkBuilder <span class="caps">XML</span>:</p>
<figure class='code'>
<figcaption><span class="liquid-tags-code-filename">centered.ui</span><span class="liquid-tags-code-lines">[Lines 28-45]</span><a href='/code/emeus/centered.ui'>download</a></figcaption>
<div class="highlight"><pre><span></span><code><span class="w"> </span><span class="nt"><constraints></span>
<span class="w"> </span><span class="nt"><constraint</span><span class="w"> </span><span class="na">target-object=</span><span class="s">"button_child"</span>
<span class="w"> </span><span class="na">target-attr=</span><span class="s">"center-x"</span>
<span class="w"> </span><span class="na">relation=</span><span class="s">"eq"</span>
<span class="w"> </span><span class="na">source-object=</span><span class="s">"super"</span>
<span class="w"> </span><span class="na">source-attr=</span><span class="s">"center-x"</span>
<span class="w"> </span><span class="na">strength=</span><span class="s">"required"</span><span class="nt">/></span>
<span class="w"> </span><span class="nt"><constraint</span><span class="w"> </span><span class="na">target-object=</span><span class="s">"button_child"</span>
<span class="w"> </span><span class="na">target-attr=</span><span class="s">"EMEUS_CONSTRAINT_ATTRIBUTE_CENTER_Y"</span>
<span class="w"> </span><span class="na">relation=</span><span class="s">"eq"</span>
<span class="w"> </span><span class="na">source-object=</span><span class="s">"super"</span>
<span class="w"> </span><span class="na">source-attr=</span><span class="s">"center-y"</span><span class="nt">/></span>
<span class="w"> </span><span class="nt"><constraint</span><span class="w"> </span><span class="na">target-object=</span><span class="s">"button_child"</span>
<span class="w"> </span><span class="na">target-attr=</span><span class="s">"width"</span>
<span class="w"> </span><span class="na">relation=</span><span class="s">"ge"</span>
<span class="w"> </span><span class="na">constant=</span><span class="s">"200"</span>
<span class="w"> </span><span class="na">strength=</span><span class="s">"EMEUS_CONSTRAINT_STRENGTH_STRONG"</span><span class="nt">/></span>
<span class="w"> </span><span class="nt"></constraints></span>
</code></pre></div>
</figure>
<p>Additionally, I’m writing a small parser for the Visual Format Language used
by Apple for their own auto layout implementation — even though it does look
like <span class="caps">ASCII</span> art of Perl format strings, it’s easy to grasp.</p>
<p>The overall idea is to prototype UIs on top of this, and then take advantage
of <span class="caps">GTK</span>+’s new development cycle to introduce something like this and see if
we can get people to migrate from GtkFixed/GtkLayout.</p>