Barium 0.2 - What's new?
In my last article I introduced Barium, the X toolkit natively written in Common Lisp. After releasing it this April, I moved on to other, yet to be published projects, and then to having a great summer. I finally came back to Barium in late August. From then up to now, I spent about ten weeks of mostly full-time work bringing the toolkit one notch further. The result is the second release of Barium, bringing lots of cool new features. Here’s a summary.
Menus!
Let’s start with the big game. Barium now provides menu bars (to be used at the top of windows as a main application menu) to open up potentially hierarchically embedded (cascading) menus:

The same kind of menu can be popped up on demand (via right-click or whatever mechanism preferred by the application):

Menus are built from a simple recursive descriptor (list structure) supplied by the user application. To understand how to use this feature, take a look at the example source.
Menus provide support for:
- seamless navigation with the mouse and arrow keys
- hotkeys (auto-registered as window-global hotkeys; displayed in Emacs notation)
- shortcut keys (underscored with red; flashed on menubar while pressing and holding Alt)
- icons
- tick-boxes
- radio selectors
- separators
Panes!
Panes are helpful when arranging complex user interfaces. They help the user feel in control as they allow seamless resizing of their subdivisions.
A pane can be split horizontally (a hpane) or vertically (a
vpane), and can contain one, two, or even more child widgets:

This little test program (example source) presents multiple horizontal panes stacked in a vertical pane. The image above is a composite showing the cursor over all pane dividers.
A notebook!
Similar to a pane, a notebook can be helpful when arranging complex user interfaces with many elements. Contrary to a pane, a notebook shows only one of its children at any moment.

The notebook allows runtime management (inserting and removing) of its children that make up the pages, and also supports custom widgets placed into the selector tabs. The above example (source) demonstrates this with tabs that contain a label and an image.
Dialogs!
Barium now provides a small (but hopefully growing) set of dialogs ready to be invoked by the application. They are nothing magical, they are just like any application code you would implement yourself. (Example source)
Message dialog

The message dialog contains a textview and a button to close the window. It can be invoked by providing the text (and optionally any other arguments) of the textview as well as (optionally) the title, the caption of the close button, and text area dimensions (width and height). If any of the latter are provided, the text will be scrollable in the respective direction; if omitted, the window will assume the size required by the textview.
File chooser dialog

This dialog lets the user browse the filesystem (starting from an arbitrary directory) going up to the parent or down into any subdirectory; to apply filters specified by the application; to toggle the visibility of hidden files; and to select a file (unless hidden by the chosen filter).
Double-clicking on list entries (or pressing Enter while they are
selected) invokes Select, which concludes the dialog or enters the
selected subdirectory.
When invoking the dialog with :freeform-entry t an additional entry
for entering an arbitrary filename is included. A variant of an
existing file can quickly be specified by clicking on it in the list
(populating its name into the entry), then editing it. A new
subdirectory named after the entry text can also be created by
pressing + Folder. This is suitable for using the dialog for picking
the destination of “Save As…” operations.
A flexible event loop!
Real-world applications need support for managing and reacting to events other than just those originating from the X Window System itself. Generally, such events indicate a piece of input data becoming available to the application, having arrived over some communications channel (e.g., a TCP socket). Another common case is when an application needs to perform an action in a timed manner, either in a recurring fashion or by deferring it for some time into the future. Reaching such a deadline is another kind of event. Finally, applications might have a need for running code “in the background” or as an “idle task”, as frequently as possible, without compromising the application’s responsiveness to external events.
To support all these use cases, the main event loop (tasked with processing and dispatching X Window System events) is now a first-class object in Barium. It is made available for applications to manage (add or remove) their own handlers to various events on demand. This capability supports event-driven (async) programming, allowing the creation of highly efficient yet comprehensible programs without the need to run concurrent activities in separate threads (and the added complexity associated with synchronizing and communicating between them).
Handlers can be registered for:
- file descriptor read and error events
- periodic (recurring) timer events
- deferred (run-once) timer events
- idle events (to be run as frequently as possible)
An example (source) demonstrating timed events, both periodic and deferred:

A separate demo, the chatroom, demonstrates I/O multiplexing on file descriptors (network sockets) in the Barium event loop. Running all code in a single thread avoids the many traps and complexities of coordinating between threads and makes the program much easier to reason about. It is also the best design for high performance applications!
shell
With Barium’s newly added ba-ext:shell facility, it is possible to
create an inferior process to run an external program concurrently
with Common Lisp. The standard input, output and error of this process
is connected to file descriptors accessible from Lisp. These
descriptors can be used to plug user-defined handlers into the Barium
event loop, allowing you to build a fully event-driven (async)
application where GUI events and I/O are handled by the same
thread. To perform actual I/O from Lisp, use make-fd-stream
(hopefully supplied by your Common Lisp implementation) to obtain a
stream.

This example program
(source)
runs /bin/sh and allows you to interact with it. Note that this is
not meant to be a functional terminal application, just a small
demo!
N.B.: Barium provides its own shell facility because existing
solutions such as uiop:launch-program do not offer access to the
underlying file descriptors of the I/O streams between Lisp and the
external program. Since Barium’s event loop is based on the POSIX
poll() facility, a file descriptor is required for each input
stream.
Other improvements
The below list covers much of what is left, but is still not exhaustive – some fixes were omitted for brevity.
Grid: fixed aspect ratio of child widgets
The grid (Barium’s layout manager) is now able to accommodate aspect ratio constraints (width per height) as specified on its children, as the below example demonstrates (source):

When using this feature, it is up to the application programmer to set up the grid in a sensible way. For example, the grid ought to have some rubber-band (non-constrained) area in both directions that can freely take up any extra space. Also, the initial subdivision of the grid should comply with the requested aspect ratios. This can be achieved either by appropriately set base size on the widgets’ part, or manual setting of grid row/col min-size constraints.
In the general case, it is clearly impossible to satisfy all constraints. The constraint solver works on a best-effort basis and concludes in a limited maximum number of steps even if the achieved fit remains sub-optimal. In practice, the fewer the number of aspect-constrained widgets, the better results you can expect.
Double-clicks!
There is no double-click on the X event level. Double-clicks are a
purely synthetic construct, based on a temporal pattern of multiple
button press and release events. Barium now recognizes such a pattern
as a double-click, given that the interval between the first button
release and the second button press is less than
*double-click-threshold-seconds*, that the button is the same in
both cases, and that the pointer did not move during this interval.
The button-press-event and button-release-event both have an
additional field named double, carrying a boolean value. This is
nil for all cases except for the press and release of a
double-click, which carry t.
Thus, normal handling of button events need not concern double-clicks
at all; the double flag on these events can be diregarded. In other
words, both the first and the second click of the double-click will be
seen as ordinary button press and release events (just as before), and
there is no separate event for the double-click. In case a
double-click handler is desired, simply set up a button-press-event
handler, and look at the double flag. If that is t, react to the
double-click.
Scrollbars!
Yeah, my favourite widgets ever. They received a little polish to make them look better. (Can you spot the difference?) More importantly, I added a timed repeat of the scroll-step action when the mouse button is kept pressed down on one of the arrows. This works just like typematic repeat: a longer initial delay (2/3 seconds) followed by a shorter repeat delay (1/10 seconds) and is built on the newly added event loop features.
Altered X focus model
The X focus model of Barium-created toplevel windows has been altered from Globally Active to Locally Active.
Due to this change, Barium windows will no longer get raised automatically when they receive pointer focus (the mouse pointer is moved over them). Rather, they behave like most other application windows: they receive focus when the pointer enters them, but a click is required to raise them (bring them forward). Of course, exact behaviour is up to the policies of your window manager.
Another consequence is that after a Barium application quits and all its windows are destroyed, the focus is properly moved to whatever application window is underneath. Previously the focus was lost (set to None) and one had to explicitly focus another window. Again, this is more in line with how most other application windows behave.
Performance improvements
The biggest improvement is due to caching (memoization) of calls to
cairo:text-extents. The native call into Cairo proved to be a
CPU-hog especially when repeatedly reflowing lengthy text in a
textview being resized by the user (generating thousands of calls over
the same set of strings with the same font). The cache is periodically
cleared behind the scenes so you should not need to worry about it.
Shout-out to cl-flamegraph which made it an absolute breeze to identify hotspots!
Breaking changes
-
The
refresherclass has been removed; the per-frame refresh callback must be implemented using Barium’s event loop facilities. Take a look at the Gears and Shader VAO examples, or the molview demo for inspiration. -
Widget class key event handlers (that is,
on-eventmethods specialized onkey-press-eventandkey-release-event) are now expected to return a boolean indicating whether the event was acted upon. If the return value isnil, the kbd-arbiter will then dispatch the key event, potentially invoking globally bound key handlers. This allows a widget to selectively override a globally bound key handler, carving out local use of an otherwise occupied key when the widget has focus. It is important that a widget’s key event handler returnstonly if the actual key event was significant to the widget. This way, any other key events (not interesting to the widget) are still let through to activate globally bound handlers, e.g., menu hotkeys.
Conclusion
Ten weeks is a long time! I can barely remember how Barium looked like before I started on this batch of work, because so much of it is new. I really enjoyed working on these widgets and capabilities – but now I look forward to something else…
As hinted before, I am (still) working on some actual applications using Barium, and plan to publish one of them pretty soon. Apart from being (hopefully) interesting in its own right, it will also provide more insight into how to use and build on top of Barium in the context of a larger desktop application.
For now, though, you need to be satisfied with the demo apps and small test programs that come with Barium itself. They are all conveniently linked to from the Barium Gallery.
Visit the main project page of Barium for instructions on obtaining and installing the toolkit. If you play with it or use it in any capacity, I would love to hear from you!