Tiny-wlhs, a hybrid haskell and C Wayland compositor

Tiny-wlhs: A Haskell-Driven Wayland Compositor (Seeking Contributors!)

This thread originated in the Haskell Wlroots Bindings discussion.

Introduction

tiny-wlhs began as an experiment to explore Haskell bindings for wlroots. We took the simple tinywl C compositor and turned it into a Haskell-controlled shared library. This approach lets us incrementally port C code to Haskell while keeping a working window manager. The immediate feedback from a functional compositor is invaluable during development and provides something solid to work with. You can find the project on GitHub: https://github.com/l-Shane-l/tiny-wlhs

tiny-wlhs currently creates a Wayland window and serves as a platform for testing future Wayland bindings. A pure Haskell compositor presents some key challenges and is an eventual goal but first we need robust bindings for wlroots and, crucially, a Haskell equivalent of the Wayland scanner (which converts Wayland protocols from XML to C).

Motivation: Towards a Haskell Wayland Daily Driver

While waiting for bindings and a scanner there is interest in a usable, Haskell-based Wayland compositor—a daily driver that we can use and refine every day. While work on bindings and a scanner continues, tiny-wlhs provides a valuable stepping stone with the following benefits:

  1. Hands-on Learning: Building a compositor is the best way to learn Wayland.
  2. Identifying Needs: A Haskell-based compositor clarifies the requirements for a pure Haskell implementation.
  3. Top-Down Approach: Starting with a working example and iteratively improving it is a lot easier than building from scratch.

Our primary goal is to make tiny-wlhs a practical daily driver. We’ll gradually port C components to Haskell, paving the way for a future pure Haskell version. This will require careful API design. tiny-wlhs is also a tool for developing and testing Haskell wlroots bindings and a Haskell-based Wayland scanner.

Why a Hybrid Approach?

Creating complete Haskell wlroots bindings and a Wayland scanner is a monumental task. wlroots is a large and complex C library. Building a Haskell Wayland scanner is equally challenging. Our hybrid approach allows us to make progress now with a functional compositor, learning as we go and using tiny-wlhs as a testbed for future pure Haskell solutions. The project uses nix so it should be quite fast to get started.

Next Steps and Features

tiny-wlhs currently has the same features as tinywl (tinywl documentation). We’re looking to add:

  1. Core Functionality: Start/stop server, server configuration.
  2. Output Management: List outputs, basic configuration.
  3. Window Management: Tiling, resizing, moving, focus, closing, fullscreen.
  4. Input Handling: Keyboard and mouse bindings.
  5. Application Launching: Spawning applications.
  6. Compositor Features: Basic decorations.
  7. Inter-Client Communication (Future): Advanced Wayland protocols.

Get Involved!

  • Feature Development: Build the features you want.
  • Haskell FFI: Improve the Haskell/C interface.
  • Create Wlroots bindings: Create and contribute wayland bindings
  • Wayland/Wlroots: Port C code to Haskell.
  • Build Systems: Enhance the build process.
  • Haskell GUI Libraries: Design the Haskell API.
  • Join the discussion: Contribute to everything from project naming to project managment

Collaboration

This thread is for updates and discussions. We may create a dedicated communication channel (Discord, Matrix, IRC - let us know your preference!). Documentation and project details are open for discussion.

Try building and running tiny-wlhs! Report any issues on GitHub. Create branches for development. We’re excited about this project and invite you to join us!

26 Likes

Update - Addition of Keybindings

Hi all, this post and thread will likely be a mix between devlog and announcements.

This weekend keybindings was added to Tiny-wlhs which is a key milestone on the path to getting something that can be used as a daily driver.

You can see a short demo on the README GitHub - l-Shane-l/tiny-wlhs: A Wayland compositor written in Haskell, providing a configurable and programmable window management system. This project implements the TinyWL reference compositor with Haskell bindings, allowing for dynamic configuration and control through Haskell.

Besides adding the key bindings support I restructured the project to make it easier to use. Now you can configure everything from the Config.hs file

This also is documented in the Readme.

Basic Usage

Default key bindings:

Mod + Left Click: Move window
Mod + Right Click: Resize window
Mod + Esc or Alt + C: Close server
Mod + s: Open new terminal (configurable, default: kitty)
Mod + d or Mod + v or Mod + F1: Cycle between windows

And of course all of these can be configured in Config.hs

Try it out

git clone --recurse-submodules https://github.com/l-Shane-l/tiny-wlhs.git
nix-shell  # Or use direnv: direnv allow
cabal run

Notes on the key bindings

I would have liked to use something like xkbcommon, however the bindings seem to be out of date(10 years i think) and the nix pkg broken. This is actually a nice project for anyone who was interested. It will likely be needed for any Wayland Haskell bindings.

Next steps

Window management is the next thing I want to address, some basic tiling would go a long way (for me at least) to start using this as a daily driver.

Beyond that I think multi-monitor support and integrating something like bemenu would be a real step forward, and must have features.

I’m pretty happy with the progress so far and I will likely post here next when I have something to show for the window management. :slightly_smiling_face:

15 Likes

Hi Shane, I appreciate your effort. I can’t wait for this to be daily driver-able. I wanted to ask how you plan on implementing multiple desktops/workspaces in regards to multiple monitors. In my opinion the killer feature of XMonad is that Mod+1 will bring desktop 1 to the monitor I have selected. The other wayland compositors haven’t been able to really replicate that. Is that the plan with your project?

Secondly, is the goal to have full feature parity with XMonad? Will I be able to use any XMonad plugin on your project at some point? I’m sure it’s more complicated than that but I’m wondering what your thoughts are.

4 Likes

This is false in my experience — Sway can do it quite easily, with something like:

[workspace="1"] move workspace to output current; workspace number 1

As I recall, compatibility with XMonad is not a goal at all: there is no expectation that the interface will be the same as that of XMonad, or that people will be able to use their existing XMonad scripts without modification.

1 Like

Full feature parity isn’t possible because Wayland doesn’t e.g. support className, and any contribs that would work with Wayland would probably have to be rewritten to use its API (which even if not used directly would be leaked by the core).

2 Likes

Hi twiclo, I appreciate your appreciation :grinning: .

With the multiple monitors this is something I also want, my plan was to do it after adding Copy & Paste support. I opened an issue on it on the repo, along with the other planned work.

With the Mod+1 I agree, can you please open an issue on the repo requesting it? Something like “I as a user would like” and just define what you want.

I think someone else mentioned sway and I would suggest looking into it. Tiny-wlhs isnt really in a place to compete with the likes of sway. There are a number of issues even outside the missing features, for example currently it uses pixman as a renderer so its not even getting any acceleration.

The intention of the project is to provide a Wayland compositor that can be configured and controlled in Haskell. I also hope that over the coming months and years it will help in charting a map towards Haskell wayland bindings and a truly pure Haskell compositor.

On feature parity with XMonad being the goal, the answer is yes, and I would include performance with that, but this does not include the config or plugins. On the plugins its simply not possible on wayland, as its a totally different architecture. So most X11 apps have Wayland versions or Wayland alternatives, for example Dmenu has Bemenu for wayland.

Generally with tiny wlhs im torn between a few different directions for what I should focus on. I was trying to just power towards something I could use as a daily driver, but this work is just adding functionality to the tinywl.c shared library to bind to, currently this has ballooned that file to 1.6k lines and likely with the remaining Copy & Paste, Mulitmonitor and tiling support will become unmanageable.

Already the code is hard to wrangle. I have been putting of adding the necessary tests and other supporting work to make the C easier and safer to work with. I have a few options, I could just refactor the shared library into a more manageable setup, and this is likely what I will do.

However in the last few weekends I’ve been experimenting with language-c and other libraries to see if there is something clever i could do. In a perfect world I could move this work to Haskell, but keep C’s performance, code generation seems the mostly likely way to achieve this.

One of the things I was playing around with was working with the C AST. I have a few more ideas I will test this weekend, and I want to investigate what other bindings libraries like GTK did, if I can’t find a sensible path forward I will likely just refactor the C code.

In parallel to this I want to make the library more usable and easier for others, I want to add in a CI/CD, tests, better documentation etc. My hope here would be getting contributions to divide the work load a bit. This is also the reason I want to try and move the work into Haskell and not just writing C.

Im also conscious of long term sustainability it will need good testing and CI/CD, documentation etc and this become more true as time goes on.

4 Likes

I don’t really get this… to my understanding, the original tinywl was never meant to be a daily driver. It was meant to be a minimal example of how to write a Wayland compositor with wlroots. And I think tinywlhs should have the same goal. You can’t write a daily driver without first knowing that the minimum functionality works!

I think the confusion is my fault, looking back over this thread I should have been more active with my updates.

What you describe as the original tinywl was complete long ago, even before I started this thread, I converted it to a shared library and had feature parity with tinywl on the first weekend. The basic tinywl does not do much, so I began to add to it.

Because tinywl as a shared library runs wayland scanner on the protocol and wrangles all the dependencies from wlroots and wayland, its incredibly easy to just bind to a tinywl c as a shared library.

Further because most wayland compositors are written in C or C like languages it much easier to develop compositor features in C and then bind to Haskell instead of trying to do it in pure Haskell with bindings. There is also performance gains because anything that does not need to be controlled, moved or set in Haskell can be done in C.

So if you look at the README and the features you will see a lot have been added. For some features like third party app support or layering it makes a lot more sense to do these in C, for other things like spawning apps or keybindings it makes a lot of sense to do in Haskell.

So with the hybrid approach the best of both worlds can be leveraged. With features that users what to set, configure or integrate Haskell is perfect, for integrations, performance or other work away from the view of a user these are best done in C.

This approach removes the complexity of first creating blanket bindings for wayland, and then trying to figure out how to use them. Support and implementation can start in C and move to Haskell only if needed.

And further if Haskell bindings for Wayland appear in the mean time, the project is set up to take full advantage of them, the C and Haskell divide is not set in stone, any or all of the C can be converted into Haskell. This could even be a fruitful way to create bindings. However for me right now the priority is features.

So the goal is to create a daily driver and to systematically develop/iterate towards a robust and compelling daily driver in the coming weeks and month and years. Its already pretty close to a something I will use daily.

I cannot see any reason this is not possible, you could argue about the implementation or the functional purity or similar but for me personally I want a compositor I can configure and build-on in Haskell, I want it to be feature-full, performant, flexible and easy to use and this is what I intend to build.

3 Likes

Hi, xkbcommon dev here. What is the progress with the xkbcommon bindings? I would be interested to support an official Haskell lib.

5 Likes

I’m more so talking about xmonad modules like ManageDocks.

Well yes but sway doesn’t do dynamic tiling.

I dont know what the status of xkbcommon bindings are, I only know of this one.

For the tiny-wlhs compositor, I just hard coded some values as a hack to support keybindings tiny-wlhs/src/LibTinyWLHS/KeyBinding/KeySyms.hs at main · l-Shane-l/tiny-wlhs · GitHub

If good bindings existed I would definitely switch to using them

So there is some basic for apps like bemenu and yambar. But for any feature requests its best to add it as an issue on github and have the discussion there.

Also I should manage expectations, currently I spent a few hours each weekend working on this, and im focused on refactoring the C shared library and adding in tests etc atm. So outside of holiday periods or time off, development will be slow but steady. Atm its really just for people interested in hacking on Wayland in Haskell.

Ill just keep my posts on this forum for milestones or when there are enough new features to report on.

3 Likes