Recently, I launched my Wayland shell project, Scarlet. The main repository is hosted on gitlab at Scarlet.
Aiming to be a solid option providing modular widgets, a full shell experience, SCSS-based styling and a small binary, Scarlet happens to be a culmination of many months of experimentation.
Previous attempts
SnowyShell: The beginning
The first of which was SnowyShell, which in my honest opinion was not the best shell out there. At this point in time, my plan for SnowyShell was to create a shell with Astal, implemented in Lua, using Rust modules. This turned out to be a pretty bad idea, it turns out that compiling multiple Rust crates as modules for a Lua-based project, which was not compiled is a pretty time consuming task.
WxShell: The abandoned
Soon I desired to turn to a language where I could implement the entire shell. Which lead me to play around with Vala. I then created WxShell, which I quickly abandoned in my lacking of Vala knowledge.
ZShell: Also abandoned
Shortly after that, I decided that I would try an implementation in Zig, in the form of ZShell, which I abandoned as soon as I realized that I had to write the libraries from scratch. At the time, Zig bindings for DBus and GObject where not very useful. That has changed, as I explore in this post Zig bindings for GObject-based libraries.
Waycute: Abandoned
I then created yet another project, this time implemented in QML, based on QuickShell. Going by the name WayCute. This too was abandoned.
Crimson: The dawn of a new era
In light of WayCute’s abandonment, came the idea for a new project, which became a massive inspiration for Scarlet. This project was Crimson. This was my first serious attempt at putting together a Wayland shell. Crimson, like Scarlet, was an Rust-based implementation on this idea. The idea was to create a shell which could be configured in Lua.
Original implementation
Crimson 0.2.0 provided built-in modules. I archived the repo on Codeberg for those who may be interested in seeing Crimson in it’s earliest form.
The enhanced, Scaffolding variant
Crimson was then rewritten, as Crimson 0.3.0, which took the Lua-based configuration to another level. Crimson thus became a framework, much like Astal, using lgi as a backend for providing GTK widgets and GObject support.
Unexpected problems
Unsafe pointer casts
This came with many costs
however, the biggest being the requirement of unsafe code to deal with casting
GTK objects in Lua into Rust types. This quickly began to become a problem,
dealing with pointer objects such as GList proved to be impossible as it was
virtually impossible to identify the type of a pointer.
Object classes in Rust
Worse so was the experience of creating GObject Objects in Rust. All custom modules crimson used were planned to be written in Rust. Which is not easy whatsoever. The project gobject-example-rs provides insight into how this can be done. It is doable for a small subset of modules, but needing to create C headers for every module I was planning to write, along with creating scripts to deal with the gir bindings was out of the question.
There is, as of writing, an open issue about this problem.
Then came Scarlet
Astal Rust bindings
Then, one day, I came across an epiphany. The realization that I could create Rust bindings for Astal came to me. I thus kicked of the project Astal-rs. Which are available on crates.io. These were generated using the gir tool. It took a bit of effort putting together the dependencies for the bindings like JsonGlib and libnm though.
Astal-rs thus became an inspiration for Scarlet.
Configuration language
As I went through the features provided by Scarlet, one of my requirements was an easy to understand configuration language that is easy to parse. TOML was the first to come to mind, but I do not find TOML aesthetically pleasing. As I sifted through options like JSON, RON and Lua, I came across Niri’s configuration language, being KDL.
I am a big fan of the structure of KDL and find a pretty easy language to work with. Thus I came to use KDL as the configuration language for Scarlet.
Small binary obsession
One of my requirements for Scarlet is to have as small a binary as possible. This was not possible with Crimson, which continued to increase in size with the first implementation. This was a result of using lua bindings and dbus bindings, which taking a look at Cargo bloat happened to take up over half the binary size!
With Scarlet however, this concern has mostly been cleared and it is safe to say that the Scarlet binary shall never rise above 10Mb in size.
Moving forward
I spent hours daily for 14 months trying to put together a shell that fit. While I will not proclaim that this was the best way to spend a year, it was certainly a great learning experience. With my efforts focusing on Scarlet, I also have time to work on other projects which had been sidelined during my shell adventure, so expect updates on those soon.