[Gubar] Swaybar Generator, Written in Guile

Hi everyone,

Recently, I’ve set up a new Guix System on a Thinkpad and I decided to migrate over to Wayland and sway after being a longtime bspwm user on X11. For a top bar/status bar on X11, I use polybar, which is similar to waybar on Wayland.

I installed waybar and tinkered with it for a few minutes, but I wasn’t too keen on fiddling with CSS and trying to emulate my polybar configuration, all while wrangling and learning Guix to make it usable for me.

Once I remembered (from my i3 days) that there is a swaybar protocol (see i3blocks) I decided that I would take a more interesting approach and write my own version of a swaybar generator using Guile Scheme – Gubar (pronounced “goober” and named by @daviwil)

High-level design overview

After looking at the swaybar-protocol spec, I realized that I would need guile-json for serializing updated bar components (blocks), and also for deserializing click events that are captured by sway and dumped on stdin of the swaybar command (in this case, gubar).

The bare minimum bar usually sleeps for one second and then runs one or more shell command and parses the outputs into a nice long string, but that becomes quite inefficient if you have many blocks or a command that can take a bit longer to run. You could instead sleep for one minute, but then you don’t get instant updates on things like volume, cpu, battery, etc – IF those things are important to you.

I thought it would be more appropriate to use fibers and have one fiber per block. The fibers are kinda like greenthreads or goroutines, but based on “Concurrent ML”. Andy Wingo has a really nice write-up on it in the fibers manual.

With this design, I can sleep each block for different amounts of time, having them run asynchronously, and then message back to the main scheduler when they update so we know when to output the fresh set of blocks.

Configuration

Unlike i3blocks, I want gubar to be configured fully in Guile. Right now, it just calls load on the config.scm file, which I’m not entirely sure is correct, but it works well enough. This way made sense to me since it’s as if the config is just another module, that may or may not be present. If you have tips on this, I’d love to know!

I really like the ergonomics of the syntax that Guix uses for defining system or home configs. They have a custom record type that is, in my opinion, more robust than srfi-9 records. I don’t want to depend on Guix or copy paste out the definition, so right now the configuration is a bit clunky. If you have any ideas or would like to help make it more comfortable, I’m open to suggestions and collaboration.

Simple DateTime Block

As an example, here is a module for displaying the current time:

DateTime block code

link to src

(define-module (gubar blocks date-time)
  #:use-module (gubar gublock)
  #:use-module (gubar swaybar-protocol)
  #:use-module (ice-9 textual-ports)
  #:export (date-time))

(define* (date-time #:key (format "%c") (interval 1))
  "Creates a new date-time gublock."
  (gublock
   #:interval interval
   #:procedure
   (lambda (block)
     (scm->block
      `(("full_text" .
         ,(strftime format (localtime (current-time)))))))))

Due to guile-json not exposing modifiers on the generated srfi-9 record types, I opted to use an assoc-list and then convert it to the record type (scm->block). Eventually, I will have to implement the functional modifiers manually to avoid this.

Checkout the other modules for some (slightly) more interesting examples. I am slowly hacking on the ones that I will be using in my daily driver config.

Screenshot

It’s just the regular swaybar with some nerd font icons, but here’s a screenshot with the config:

Still needs a lot of work, but it’s getting there!

Happy Hacking!

9 Likes

This looks fantastic, huge potential! I’ll bump it on the stream this Friday.

1 Like

I agree with @daviwil @trev it does look fantastic. And it’s written in guile scheme. Great effort and excellent idea. I have forked your repo and hope I can contribute.

Just brilliant.

1 Like

That looks great, kudos for doing it in guile scheme! Went a different route myself by using my emacs modeline as a status_command for a STDIN-based status bar and writing a minor mode to keep it updated → theesm/modeline-statuscommand.el - Codeberg.org, but using guile to do so definitely is a lovely and cool thing!

Do you have any plans on using NetworkManagers dbus interface instead of nmcli to obtain network information? Am currently working on a minor mode in emacs displaying these things using elisps dbus functions (which is quite fiddly); and as guile has Gwen Weinholt / AC⚡D-Bus · GitLab that could be a good thing to dive into^^

1 Like

Seems cool! Definitely seems comfy to have everything on the emacs statusline.

I haven’t looked into NetworkManager and dbus yet. Was hoping to keep all the commands as simple as possible.