``` 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`