This is the monorepository for the boring Wayland compositor. As the name implies, it is boring by default. However, the hacker in you will probably love it.
Warning
This is an experiment in developing a LuaJIT-based Wayland compositor. It isn't ready for serious desktop usage. I'm not currently working on it (as of June 28, 2026) and there's a lot of clean-up that must be done before feature development is continued.
boring is a minimalist, non-monolithic Wayland compositor built on wlroots that is designed to be hackable and extensible, reminiscent of dwm and dwl. Functionality is provided through plugins; we give you the tools and leave you to play.
A set of default plugins are included that provides basic window management and Wayland functionality for those that don't want to spend too much time writing their own:
borders: Draw borders around windowslayout-stacking: Traditional stacking layoutlayout-tiling: Sway-inspired n-ary tiling layoutlayout-kiosk: Runs a single, maximized windowscreenshot: Screenshot pluginwallpaper: Wallpaper pluginxwayland: XWayland integration
The default configuration automatically mounts borders, layout-stacking, screenshot, wallpaper, and xwayland. The documentation for each plugin can be found in their respective files.
The compositor is entirely written in LuaJIT. For this reason, using and developing the compositor is the same process, as you'll be running the compositor from source.
To get started with boring, you need the following dependencies installed:
- LuaJIT
- The most recent version of
wlroots, which at the time of writing is 0.20 wlr-protocolsandwayland-protocolsinstalledgccandpkg-config
Some additional dependencies may be needed depending on the plugins you choose to use. The default plugins depend on xwayland, which requires xorg-server and libxcb, as well as libvips for the wallpaper and screenshot plugins.
On Debian 13 hosts, use the provided Arch container for development if you want the current wlroots target:
podman build -t boring-dev -f Containerfile .
podman run --rm -it -v "$PWD:/workspace" -w /workspace boring-devTo try the compositor from the container in your current Wayland session, run it nested through the host Wayland socket:
scripts/run-wayland-containerThe helper rebuilds the development image, then uses wlroots' Wayland backend and
should open boring as a regular window on your host compositor. It defaults to
the software pixman renderer so it does not need GPU device passthrough. By
default it uses
configs/nested-wayland.lua, which avoids host-reserved Super-key shortcuts,
launches xeyes once on startup, and binds:
Alt+ReturnorCtrl+Alt+Return: launchxeyesAlt+q: close the focused windowAlt+Shift+qorCtrl+Alt+q: exit the compositor
To try GPU rendering instead:
BORING_USE_DRI=1 scripts/run-wayland-containerPass a different config with:
scripts/run-wayland-container -- --config=default.luaTo download the compositor, clone the repository, then use switch.lua to launch the compositor program:
git clone https://github.com/loukamb/boring.git
cd boring
./switch.lua compositorNote
The first run will take a second or two as it compiles the FFI cache. Subsequent runs will be much faster.
The dependencies for development are the same as for usage. If you can run the compositor, you're good to go.
/compositor: Source code for the compositor/clients: Tiny programs providing basic desktop functionality/shared: Common utilities shared across the programs in this repo/plugins: Default plugins/website: Website for the project
Tests are split by how much compositor machinery they need:
tests/unit: pure Lua and parser/config tests.tests/service: wlroots-adjacent service and plugin tests using fake objects or direct helper APIs.tests/integration: headless compositor scenarios driven throughtests/harness.
Run the full suite locally with:
luajit tests/run.lua --suite allCI uses TAP output:
luajit tests/run.lua --suite unit --format tap
luajit tests/run.lua --suite service --format tap
luajit tests/run.lua --suite integration --format tapLifecycle hygiene check:
rg "create_listener|destroy_listener|ffi\\.cast" compositor pluginsThis should return no matches. Listener ownership in compositor services and plugins should go through shared.scope; raw listener helpers and casts belong in shared/wayland and targeted listener tests.
To write a plugin, put a Lua file in the ~/.config/boring/plugins directory that returns a table with a name field alongside mount and unmount functions. You can also create a folder and move your plugin's entrypoint to $DIR/init.lua if you have to ship multiple files. To enable your plugin, add it to your configuration file:
-- This will load and mount the plugin.
-- :mount() returns the plugin object.
local my_plugin = plugin("my-plugin"):mount()The second argument of plugin.mount will be passed to the plugin's mount function, which allows you to pass configuration to the plugin:
-- config.lua
local my_plugin = plugin("my-plugin"):mount({ value = "hello" })
-- plugins/my-plugin.lua
function plugin:mount(config)
print(config.value)
-- This value will be returned to the :mount() invoker.
return { a = 1 }
end
return pluginPlugins and configuration possess the same environment, so anything that's possible in configuration is possible in plugins. Technically speaking, your configuration file is a plugin with simply special considerations.
For examples on how to write plugins, see the default plugins.
BSD Zero Clause License
Copyright (c) 2026 Louka Ménard Blondin <hello@louka.sh>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.