277 lines
24 KiB
Markdown
277 lines
24 KiB
Markdown
# Kutu WM
|
|
|
|
## What is a window manager?
|
|
|
|
A graphical desktop is made of many separate programs (a browser, a terminal, a file manager) all sharing one screen. Each application only knows how to draw its own contents inside a rectangle called a window. They have no idea where that window should sit on the screen, how big it should be, or what should happen when several windows overlap. This is the job of a window manager (WM).
|
|
|
|
A window manager is the software that controls the placement and appearance of windows on a graphical display.
|
|
|
|
The things a window manager is usually responsible for are:
|
|
- Placement and sizing: deciding where a new window appears and how large it is, and handling the user moving or resizing it.
|
|
- Focus and stacking: tracking which window currently receives input and which windows are in front of others.
|
|
- Workspaces: grouping windows onto virtual desktops that can be switched between. (This is not always implmented but I personally like the concept.)
|
|
- Input handling: reacting to global keyboard shortcuts and mouse actions that don't belong to any one application.
|
|
- Minimized state: WM's also handle minimized windows with sometimes a taskbar which shows these windows. (I do not plan on making this work.)
|
|
|
|
Window managers are generally split into a few categories based on how they arrange windows. Stacking (floating) window managers let windows overlap freely, like papers on a desk, which is what Windows and macOS do. Tiling window managers automatically arrange windows so that they never overlap and fill the screen, usually controlled almost entirely from the keyboard. Dynamic window managers can switch between these approaches.
|
|
|
|
A window manager is not the same thing as a desktop environment (DE).
|
|
A desktop environment such as KDE or GNOME bundles a window manager together with panels, settings tools, file managers and more.
|
|
A basic window manager does only the window management and leaves the rest to the user, which is what makes it a good scope for a project like this one.
|
|
|
|
On X11 specifically, the window manager is just another client program that talks to the X server. It is not built into the display system. It asks the server to redirect window requests to it, so that when an application asks to show or resize a window, the WM gets to decide what actually happens. This is exactly why X11 allows replacing the window manager entirely, and why I can build my own.
|
|
|
|
^ Wikipedia (2026). Window manager. [online] Available at: https://en.wikipedia.org/wiki/Window_manager [Accessed 7 October 2026].
|
|
|
|
## X11
|
|
|
|
On Windows the window management is handled by the **Desktop Window Manager (DWM)** by Microsoft. Since window management is directly integrated into the operating system, it is not designed to be replaced by users with a completely custom window manager. (although extentions can be made, they are not as complete.)
|
|
|
|
MacOS's also have a similar situation where they build and roll out the WM along with the core operating system itself.
|
|
|
|
If I want to build my own window manager, Linux provides a much more suitable environment. Linux is highly customisable, and the graphical session on linux devices can be chosen by the user, as such they can start thier own programs as the graphical session directly from the tty or through their init system (like `systemd`).
|
|
|
|
The two main display server protocols to consider are:
|
|
|
|
* [Wayland](https://wayland.freedesktop.org/)
|
|
* [X11](https://www.x.org/wiki/)
|
|
|
|
### X11 vs Wayland
|
|
|
|
Wayland (created in 2012) is a much newer Display Standard as compared to X11 which has been around since 1984.
|
|
|
|
Although X11 has recived many updates the core philosophy remains the same.
|
|
|
|
Wayland has been steadily replacing X11 across Linux distrobutions recently.
|
|
|
|
Data from [Linux Hardware](https://linux-hardware.org/?view=os_display_server) shows Wayland's adoption increasing significantly, although this shift has happened relatively recently.
|
|
|
|
KDE's own metrics also provide an interesting picture. [David Edmundson's analysis](https://blog.davidedmundson.co.uk/blog/metrics-in-kde-are-they-useful/) showed an overall split of approximately **45% Wayland and 55% X11** at the time of the analysis. However, the distribution was quite different on newer systems, where Wayland was the default: these systems showed closer to **80% Wayland and 20% X11**. This suggests that much of X11's remaining usage comes existing installations rather than from users actively choosing it over Wayland.
|
|
|
|
There are several technical reasons for this switching. X11 has gained a lot of architectural complexity over its lifetime, while Wayland was designed to address many of these limitations specifically. Wayland provides stronger security boundaries, reduces unnecessary frame-buffer copying and memory usage, and has a more straightforward approach to compositing and preventing issues such as screen tearing. These differences are discussed further in [this comparison of X11 and Wayland](https://www.linuxteck.com/x11-vs-wayland/) and in [Phoronix's discussion of the current X11/XWayland situation](https://www.phoronix.com/review/x_wayland_situation).
|
|
|
|
Despite these advantages, X11 still has some important benefits for this project. It is a mature and extensively tested system, and its architecture and tooling are well documented. For a project where I want to focus on implementing the window manager itself rather than dealing with the underlying display protocol, this maturity makes X11 considerably more approachable.
|
|
|
|
One particularly important feature for my workflow is X11's support for remote graphical applications over **SSH through X11 forwarding**. I regularly use SSH to work with remote servers, and this provides a straightforward way of running graphical applications remotely. Wayland does not provide an equivalent native mechanism with the same workflow, meaning additional technologies would be required.
|
|
|
|
### Decision
|
|
|
|
Although Wayland is the direction in which the Linux desktop ecosystem is moving, X11 is currently a better fit for the goals of this project.
|
|
|
|
Wayland offers a more modern architecture, particularly for security, compositing, and rendering. However, X11 provides a mature and well-established environment with simpler tooling for implementing a custom window manager, while also supporting a remote workflow that I already rely on.
|
|
|
|
For these reasons, I have decided to implement my window manager as an **X11 window manager**.
|
|
|
|
## Why use this WM specifically
|
|
|
|
Given various window management systems already exist for X11 why would i try to build my own? and specifically why anyone else might want to use it.
|
|
|
|
My main focus for this project is going to be on runtime customizablity of the window manager, basically the user will have the full control on how their workflow works.
|
|
|
|
It will be programmable at runtime where they can change the behaviour and add keybind based triggers too.
|
|
|
|
This will need to be done by a runtime embedded scripting system.
|
|
|
|
## i3
|
|
|
|
i3 arranges windows in a tree of containers, which the user splits horizontally or vertically as they open windows. This gives a lot of control over layout, and it is one of the most widely used tiling window managers, with a large community and plenty of documentation.
|
|
|
|
The main difference is in how it is customised. i3 is configured through a plain text file with a fixed set of commands and directives. The user can bind keys to those commands and to external programs, but the behaviour of the WM itself cannot be programmed. Changing the configuration also means editing the file and reloading, rather than changing behaviour while it runs. i3 does provide an IPC interface over a socket that external programs can use to send it commands and query its state, but this means writing a separate program that sits outside the WM and talks to it, rather than writing logic inside it.
|
|
|
|
The WM I plan on building uses a scripting language to run the code live and allows the user to design their workflow to do anything cool they can think of in a live runtime.
|
|
|
|
^ i3 (2026). i3 User's Guide. [online] i3wm.org. Available at: https://i3wm.org/docs/userguide.html [Accessed 7 October 2026].
|
|
|
|
## Target users
|
|
|
|
The window manager is aimed at advanced Linux users who want to control their workflow through code rather than a fixed configuration file.
|
|
|
|
### Stakeholder 1: Keyboard-driven tiling WM users
|
|
|
|
Users of window managers such as `i3`, `bspwm` or `dwm` who are comfortable in a terminal, edit their configuration by hand and control most of their desktop from the keyboard.
|
|
|
|
These users already bind keys to launch programs and move windows, but they are limited to the commands the WM provides. With this window manager they could write their own behaviour, such as a keybind that cycles through windows of the same application, or a rule that automatically moves new terminal windows to a particular workspace.
|
|
|
|
### Stakeholder 2: Developers and sysadmins who work remotely
|
|
|
|
Users who regularly connect to remote servers over SSH and sometimes run graphical applications on them through X11 forwarding. I am part of this group, so I can also act as a source of requirements here.
|
|
|
|
They would run the window manager locally and expect forwarded applications to behave like any other window: placed, tiled, focused and moved between workspaces. They could also script context-specific layouts, for example a keybind that opens a terminal and an editor side by side for a particular project.
|
|
|
|
X11 compatibility (which is why I chose X11 over Wayland), workspaces to separate projects or machines, reliable focus handling and fast response to input.
|
|
|
|
Stability matters most to this group. If the window manager crashes, they lose their whole graphical session, including any forwarded applications. A mistake in a script should be reported clearly without taking the session down.
|
|
|
|
Since the project is aimed at advanced users, I am not providing a graphical settings tool, panels or a taskbar, and I do not plan to support minimised windows. These are the responsibilities of a desktop environment rather than a basic window manager, and the stakeholders above are comfortable working without them.
|
|
|
|
They can script these features in using my api and other libraries if they want however.
|
|
|
|
## Success criteria
|
|
|
|
|No.|Stakeholder need|Feature|Success criterion|
|
|
|---:|---|---|---|
|
|
|1|Change behaviour without restarting the session|Runtime mruby scripting|Editing a script and reloading it changes the WM's behaviour without restarting the WM or the X session|
|
|
|2|Fast keyboard-driven control|Scriptable global keybinds|A script can bind a key combination to an action, and it triggers every time with no noticeable delay|
|
|
|3|Automate behaviour when windows appear|Window event handling|A script can run code when a new window is created and change its position or workspace|
|
|
|4|Organise tasks and projects|Workspaces|The user can switch workspaces and move windows between them using keybinds|
|
|
|5|Predictable layout|Tiling and placement|New windows are placed automatically, do not overlap and fill the screen|
|
|
|6|Control over which window receives input|Focus and stacking|The focused window receives keyboard input and the user can change focus using the keyboard and mouse|
|
|
|7|Remote graphical workflow|X11 compatibility|An application forwarded over SSH is managed in the same way as a local window|
|
|
|8|Trust that the session is safe|Error handling|A script containing an error is caught and reported without crashing the WM|
|
|
|9|Responsive desktop|Performance|Opening, moving and switching windows feels immediate|
|
|
|10|Approachable scripting|Scripting interface|A new user can write a working keybind in a few lines of Ruby using the documentation provided|
|
|
|
|
## Language choice
|
|
|
|
This project involves two language decisions.
|
|
The first is the scripting language, the one users write their configuration and behaviour in.
|
|
The second is the host language, the one the window manager itself is written in.
|
|
|
|
### Embedded Scripting
|
|
|
|
Embedded scripting languages are programming lanaguges that are designed to be embedded in a host programming language. The embedded language is often an interpreted language while the host is a compiled static typed language. They allow adding code to the program that can be modified and interpreted at runtime. They are often used to extend on the functionality of the host language. And they also allow using functions defined in the host language which are usually much more performance optimized.
|
|
|
|
They are usually very lightweight (so that the final binary doesn't get bloated and runs fast enough). They are very useful for cuztom DSL/syntax.
|
|
|
|
We can also see a list [list](https://dbohdan.github.io/embedded-scripting-languages/) of embedded scripting languages that exist, common ones being Lua, javascript variants, squirrel, and ruby variants (mruby in particular).
|
|
|
|
These all have a C-ABI, meaning they can be used by almost any compiled language.
|
|
|
|
^ Rordrigues, C. (2023). Embedded Scripting Languages. [online] caiorss.github.io. Available at: https://caiorss.github.io/C-Cpp-Notes/embedded_scripting_languages.html [Accessed 13 May 2026].
|
|
^ Github. (2022). Embedded scripting languages. [online] Available at: https://dbohdan.github.io/embedded-scripting-languages/ [Accessed 13 May 2026].
|
|
|
|
#### What options do i have.
|
|
|
|
(https://caiorss.github.io/C-Cpp-Notes/embedded_scripting_languages.html, and mention commonality of lua etc.)
|
|
|
|
#### Why use Mruby
|
|
|
|
Why ruby is great for development for the runtime of this WM:
|
|
|
|
- Dynamic typing. it has almost no type declarations on variables, and duck typing means any object with particular behaviour can be treated the same.
|
|
- Expressive syntax. ruby has one of the most expressive syntax that improves code readability by a lot, it allows operator overloading, very clean dsl structures, minimal puntuation neccessary, and a lot of syntactic shortcuts.
|
|
- Very rich standard library. the ruby std lib has a lot of helpers for json, sockets, networking, file management, multithreading, regex etc.
|
|
- It also has a lot of inbuilt datatypes with helper methods that allow doing a lot of stuff. it has integers with no hard maximum, floating point numbers, strings and arrays, but it also has symbols, hashmaps and regex etc. for much faster development.
|
|
|
|
And as its official website itself states, ruby is easy to write, easy to read with natural syntax like spoken language.
|
|
|
|
> Ruby's expressive syntax allows you to write complex logic concisely. By leveraging powerful features like metaprogramming and blocks, you can reduce repetition and focus on solving core problems. With rich testing frameworks, you can maintain quality while achieving rapid development cycles.
|
|
|
|
> Ruby has a simple and intuitive syntax that reads like natural language. By eliminating complex symbols and verbose constructs, Ruby's design philosophy allows you to express what you want directly. With minimal boilerplate and high readability, it's friendly to beginners and maintainable for experienced developers.
|
|
|
|
For these reasons and my personal familiarity with the languge i've planned to use ruby as the languge in which the WM code is written.
|
|
More specifically we will use the `mruby` version as it allows for embedding a lightweight ruby VM into our WM that can run the scripts without needing an external interpreter.
|
|
|
|
`Mruby` is a very lightwieght impleemntation of the ruby runtime and also supports semi-compiled bytecodes.
|
|
|
|
^ Mruby.org. (2026). mruby - Lightweight Ruby. [online] Available at: https://mruby.org/ [Accessed 13 May 2026].
|
|
^ Ruby (2019). Ruby Programming Language. [online] Ruby-lang.org. Available at: https://www.ruby-lang.org/en/ [Accessed 13 May 2026].
|
|
|
|
### Host language.
|
|
|
|
I did not settle on the host language by reasoning alone. I built three prototypes, and the final choice comes from what each one taught me.
|
|
|
|
#### Criteria
|
|
|
|
Before describing the prototypes, I've set out what I am judging them on.
|
|
These come directly from my success criteria, so the choice rests on the needs of my stakeholders.
|
|
|
|
|Criterion|Linked success criteria|Why it matters|
|
|
|---|---|---|
|
|
|Runtime performance|2, 9|Keybinds and window operations must feel immediate|
|
|
|Stability and type safety|8|A crash ends the user's whole graphical session|
|
|
|C interoperability|1, 7|Needed to call xcb and to embed the mruby VM|
|
|
|Distribution|10|One program is easier to install and run than a collection of scripts|
|
|
|Tooling and iteration speed|n/a|I am the only developer, so slow builds directly slow the project|
|
|
|
|
I included tooling deliberately. For a solo project, a toolchain that costs hours each week is a genuine technical constraint, not a matter of taste.
|
|
|
|
#### Prototype 1: Ruby with FFI bindings to xcb
|
|
|
|
My first version used standard Ruby, calling xcb through FFI bindings. I started here because I already knew the language well, and it let me learn the X11 protocol (connections, event loops, MapRequest handling, key grabs) without also learning a new language.
|
|
|
|
This worked as a learning tool. I got a basic window manager running in a couple weeks, and it confirmed that my understanding of the X event model was correct.
|
|
|
|
I abandoned it for two reasons.
|
|
1. The first was performance. Ruby is not built for performance.
|
|
2. The second was structure. The prototype had grown into a collection of scripts rather than a single program, with no clear module boundaries and no compile-time checking of the layout logic. For software whose main failure mode is crashing a user's desktop, finding errors only at runtime was a poor fit. It also meant users would need a Ruby interpreter and the FFI gem installed, which weakens the distribution criterion.
|
|
|
|
Ruby was the right tool for prototyping but the wrong one for the finished product.
|
|
|
|
#### Prototype 2: Haskell
|
|
|
|
For the second version I wanted a compiled, statically typed language. I chose Haskell because a tiling window manager is largely a state-transformation problem: the layout tree, the workspaces and the focus are all data, and each event produces a new state. Haskell's algebraic data types, pattern matching and emphasis on pure functions suit this well. (A popular window manager `xmonad` is written in Haskell, which supports this reasoning.)
|
|
|
|
I built [Add actual fueature set i made here.] in Haskell.
|
|
The state model was noticeably cleaner than in Ruby, and the project looked much better written.
|
|
|
|
I stopped because of the toolchain. A full GHC installation took around 9 GB of disk space, [built it and add time taken here.] for a full build, and I found the surrounding tooling (Stack and Cabal, package resolution, version compatibility) difficult to work with. Each of these slowed down the edit, build and test loop, which matters when most of the work is trial and error against a live X server.
|
|
|
|
Haskell showed me that the style of language was perfect for this problem, but the toolchain did not suit my workflow.
|
|
|
|
#### Final choice: OCaml
|
|
|
|
I moved to OCaml because it kept what I had worked with in Haskell and removed what had not.
|
|
|
|
It kept a very similar code style, so much of my Haskell design carried over directly.
|
|
|
|
It also kept the pattern matching and immutable datastructures as-is and the recursive style functions for a layout tree is the best way to work with such a project.
|
|
|
|
But the compiler and tooling are far lighter (395MB, 463ms), and dune handles building and dependency management with far less friction than my experience of Stack. OCaml also compiles to native code, giving a single binary and solving the distribution problem that Ruby had.
|
|
|
|
However OCaml has a smaller ecosystem, and no maintained xcb bindings that suited my needs existed.
|
|
So I wrote the bindings myself using ctypes, with a few small C shims where the C API was awkward to call directly.
|
|
This was more work than using an existing library.
|
|
|
|
#### Comparison
|
|
|
|
|Criterion|Ruby|Haskell|OCaml|
|
|
|---|---|---|---|
|
|
|Runtime performance|[measurement]|[measurement]|[measurement]|
|
|
|Build / iteration time|None (interpreted)|[time]|[time]|
|
|
|Toolchain size|[size]|~9 GB|[size]|
|
|
|C interoperability|FFI gem|[your experience]|ctypes + shims|
|
|
|Compile-time safety|None|Strong|Strong|
|
|
|Single-binary distribution|No|Yes|Yes|
|
|
|How far I got|Working prototype|[~X%]|Full implementation|
|
|
|
|
Looking back, the main thing I would change is the order of work. Each rewrite cost weeks, and the real deciding factors (toolchain weight and C interoperability) were things I could have tested in a single afternoon. If I started again, I would write a small test in each candidate language that opens an xcb connection, receives one event and calls into mruby, and compare the results before writing any real logic. That said, the prototypes were not wasted: the Ruby version taught me the protocol, the Haskell version taught me how to model the problem, and the OCaml version applied both lessons.
|
|
|
|
##
|
|
|
|
then research findings for the X11 windowing system and how all teh communication etc. happens and compositors and WM's and ssh access etc. (basically the mental model behind X11's philosofy, with diagrams maybe?)
|
|
|
|
and then about DE's and what DE's that work with X11 systems exist and how they exist to make utilizing a system easier and how people take it for granted with a little secion on trying to use a a system without any WM or DE to show how hard using such systems would be.
|
|
|
|
And then focusing on WM's and how they are supposed to work with X11's event system and how much control they have over windows
|
|
|
|
then continuing that with icccm and netwm standards and their history + why standards are getting messy over time or do not apply in all cases, for example they assume WM's will always have iconic, minimized and visible states etc. (with research findings of their histories)
|
|
|
|
then explaining how a WM is not that helpful on its own so we build some basic components to make a usable system
|
|
- the components being, a simple info bar (kinda like polybar in terms of usage)
|
|
- a menu thing similar to rofi (with simple searching) but in this case reused for a clipboard manager, command runner and emoji/unicode selection thing
|
|
- and finally a notification daemon that just shows the title and body of a notification int the corner of teh screen and a little highlight in the bar
|
|
(i can easily do the whole system and i kinda have built similar stuff b4 but im more worried about teh writeup than actual project)
|
|
|
|
---
|
|
|
|
This was kinda the preface section on X11 and WM's, now we do analysis of the project i chose itself.
|
|
|
|
---
|
|
|
|
starting with what im deciding to build to actually go through this, with a section comparing xcb and the X11 library and then other x11 tools like xrandr etc.
|
|
|
|
focus on the reparenting system that is standardised among these even though it is not stricly a rule needed. (and the fact that im not doing it.)
|
|
|
|
and here focus on teh exact rules laid out by icccm and netwm and how much would be needed to get working and how much would be needed for full compliance which might not even be possible in certain situations
|
|
|
|
then a final concluding chart of what needs to be coded with diagrams of the system states and how they connect and teh data needed to be stored and ill be building a mostly tiling but with floating support and workspace divided system identical to i3. (basically a requirements list)
|
|
|
|
then actual implementation of the core WM with development logs of what im actually doing with screenshots errors, fixes and rewrites id have to do because of rending issues etc. (huge section here)
|
|
|
|
while using O notation to show what complexity every component has and maybe even one or 2 graphs of performance/space somparisions for teh sake of it
|
|
|
|
Then a sort of indev testing report of what works what changes i had to make to the initial requiremnts due to complexities arising etc.
|
|
|
|
then a section on what i learnt from building this system and what i could do more and how it makes me appriciate windowing systems and parts of our computers that most people ignore while actively using
|