1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
|
```
Ladies and Gentlemen, this is
• ▌ ▄ ·. ▐ ▄ ▄▄▄▄▄▪ .▄▄ ·
·██ ▐███▪▪ •█▌▐█•██ ██ ▐█ ▀.
▐█ ▌▐▌▐█· ▄█▀▄ ▐█▐▐▌ ▐█.▪▐█·▄▀▀▀█▄
██ ██▌▐█▌▐█▌.▐▌██▐█▌ ▐█▌·▐█▌▐█▄▪▐█
▀▀ █▪▀▀▀ ▀█▄▀▪▀▀ █▪ ▀▀▀ ▀▀▀ ▀▀▀▀
```
Montis is a pluggable Wayland compositor, designed to make *hot-pluggable window
management* a thing of the present ... again!
## What Is It
Montis is intentionally split into two components, each with a very different role.
### Ark -- The Runtime
**Ark** is the small, stable executable at the heart of the system. It very
much *is* the compositor.
* It is the compositor runtime.
* It sets up wlroots, Wayland invariants, and lifecycle management.
* It persists across reload.
* It is deliberately lean: a harness, not an engine
Ark exists to keep the window system alive while everything interesting
changes around it.
### Montis -- The Window Manager
Montis is the window management logic, written in Haskell.
* It compiles to a shared object (.so)
* It is loaded into Ark at runtime
* It can be edited, recompiled, and hot-reloaded on the fly
* No compositor restart required
Montis is not a DSL and not a configuration file. It is the window manager.
Written in a Turing-complete programming language with full access to the Ark
runtime and wlroots itself.
#### Cross -- Native Bridge Library
**Cross** (`libcross.a`) is a static C library that contains the native
functions Montis uses to interact with Ark/wlroots. It is linked into the
`montis.so` world so these FFI bindings hot-reload along with the Haskell code
instead of living inside the long-running compositor process.
Code lives in `cross/src` and headers in `cross/include` (notably
`cross/include/util.h`).
### Why This is Powerful
Montis takes direct inspiration from XMonad.
XMonad is often described as a window manager “configured in Haskell”, but
that’s misleading. In reality:
* XMonad is a library
* Your “configuration” is just a cleverly hidden main
* You can rip the abstraction layer off entirely if you want
* The configuration language is arbitrarily powerful
That power is fundamentally impossible to replicate with window managers that
invent their own configuration formats.
However, XMonad has one major downside:
Because the configuration is the window manager, changing it requires a full
recompile—and a restart.
On X11, that’s fine. The window manager is a separate process from the windowing
system.
On Wayland, that model collapses. The compositor is the window system.
Restarting it kills every GUI application.
### Montis's Mandate
Montis exists to answer a simple question:
Can we get XMonad-style window management configuration on Wayland without
restarting the compositor?
The constraints are strict:
* Like XMonad, the configuration is the window manager
* Changes must not require restarting the window system
* No IPC-heavy control planes
* No bespoke configuration languages
The solution is a pluggable architecture:
* A persistent runtime (Ark) that owns wlroots and the Wayland lifecycle
* A hot-reloadable window manager (Montis) that owns behavior
## Building
### Prerequisites
- `cmake`
- A C toolchain (`cc`, `ld`, etc.)
- `pkg-config`
- wlroots build deps via your distro (Wayland, xkbcommon, libinput, pixman, etc.)
- `meson` (wlroots is built via Meson) and a backend like `ninja`
- `wayland-scanner` and `wayland-protocols`
- `curl` (first build downloads wlroots)
- Optional (only for the bundled world): Haskell toolchain via Stack: `stack` (+ GHC)
### Build
This repo uses CMake to build:
- `ark` (the compositor runtime)
- `cross` (`libcross.a`)
- the bundled Haskell world (`montis.so`) via Stack
```sh
make build
```
To build only the runtime + bridge library (no bundled world):
```sh
make BUILD_DIR=build BUILD_TYPE=Debug CMAKE_ARGS=-DMONTIS_BUILD_BUNDLED_WORLD=OFF build
```
To run via the CMake helper target:
```sh
make run
```
Note: the `run` target currently starts `ark` with `-s foot`, so you’ll want
`foot` installed (or adjust the target in `CMakeLists.txt`).
### Using Your Own World
If you disable the bundled world (`-DMONTIS_BUILD_BUNDLED_WORLD=OFF`), you can
build and supply your own world `.so` at runtime.
- World ABI: `ark/include/world.h` (world header: `ark/include/world_interface.h`)
- Optional bridge helpers: `cross/include` + `build/cross/libcross.a`
Runtime invocation looks like:
```sh
./build/ark/ark -p /path/to/your_world.so
```
## Installing
```sh
make install PREFIX=~/.local
```
This installs:
- `ark` to `~/.local/bin`
- if `-DMONTIS_BUILD_BUNDLED_WORLD=ON`, the latest built world `.so` to `~/.local/lib`
|