A utensil for chopping herbs, vegetables, or pizza, with a large semicircular blade and a handle at each end.
  • Zig 86.3%
  • Lua 13.7%
Find a file
2026-04-15 23:34:37 -04:00
docgen switch event gen to unmanaged, fix incorrect int type, use arena alloc 2026-04-15 23:20:42 -04:00
protocols update wlroots to 0.20 2026-04-14 11:00:17 -04:00
runtime allow specifying a seat to use when focusing a view 2026-04-10 23:44:09 -04:00
src add missing event docs 2026-04-15 11:58:26 -04:00
.editorconfig reformatted with zig fmt 2026-03-01 22:23:31 -05:00
.gitignore fixed fullscreen, allowing multiple fullscreens to exist at once 2026-03-11 22:56:04 -04:00
.luarc.json correct name for .luarc.json 2026-03-10 00:05:22 -04:00
build.zig Merge branch 'dev' into event-docgen 2026-04-15 11:55:17 -04:00
build.zig.zon update xkbcommon to 0.4.0 2026-04-14 11:02:17 -04:00
docgen.lua basic lua doc generation 2026-03-10 00:03:33 -04:00
LICENSE add license 2025-11-25 19:44:48 -05:00
README.md bump the wlroots version in the README.md 2026-04-15 23:34:37 -04:00

Mezzaluna

A utensil for chopping herbs, vegetables, or pizza, with a large, semicircular blade and handles at each end.

Mezzaluna is a Wayland compositor written in Zig, shouldering the complexities of compositor development, leaving configuration, windowing behavior and plugin extensibility to be expressed in easy to write, lovable Lua.

Installation

As of now, we don't package or distribute ready made binaries whatsoever.

But don't let that stop you from playing around. You can really easily build mez for yourself, ensuring you have the following dependencies:

  • Zig 0.15.x
  • wlroots-0.20
  • pixman
  • xkbcommon

Clone The Repository

git clone https://github.com/MezzalunaWM/Mezzaluna

Build and run

zig build

Install the binary

zig build install --prefix /usr

Uninstall the binary

zig build remove --prefix /usr

Configuration

Default Config

As of now, we have a default configuration located at ./runtime/share/mezzaluna/init.lua and the basics for a tiler plugin following dwm's "master/stack" layout located at ./runtime/share/mezzaluna/master.lua.

Additionally mez will also look in $XDG_CONFIG/mez for an init.lua to kickstart configuration

Custom Config

Although a default configuration is provided, you are, of course, encouraged to get creative with how your desktop experience behaves! To add custom functionality and plugins, Mezzaluna has a similar Lua configuration API to Neovim.

Interacting with Windows

Windows in Mezzaluna are called "views", and you can interface with the properties of views using mez.view. Views also all have unique ids associated with them. Examples are the best way to see what this means to the user.

mez.view.get_all_ids() -- Returns a list containing all active view ids

mez.view.get_focused_id() -- Returns the id of the currently focused view

mez.view.get_position(1011980528) -- Return the position of a view as { x = 640 , y = 360 }

-- Here 0 as a view id indicates to mez to use the current focused view id
-- 0 can replace anywhere a normal id would typically be entered
mez.view.set_geometry(0, { x = 100, y = 250 }) -- Set the position of a view to be (100, 250)

These are just the basics as of right now. More exists, but that will have to be for you to discover in the example config until some official documentation is developed for the Lua API.

Events

There are a lot of events that you can interact with and attach functions to within mez. They currently exist as keymaps, mousemaps and hooks.

Keymaps simply attach a keyboard to a set of functions for when the keybind is pressed, repeated, or released.

mez.input.add_keymap("alt|shift", "return", {
    press = function ()
        mez.api.spawn("alacritty")
    end
})

Mousemaps are for mouse interaction and can attach functions to keyboard modifiers and mouse buttons. The mousemap API provides callbacks for pressing, releasing and dragging, while providing those callbacks with extra information about the cursor. The following mousemap will move views underneath the cursor with the mouse, effectively dragging windows around the screen.

mez.input.add_mousemap("alt", "BTN_LEFT", {
    drag = function (pos, drag)
        if drag.view ~= nil then
            mez.view.set_geometry(drag.view.id, { x = pos.x, y = pos.y })
        end
    end
})

Finally there are hooks which allow functions to be used as callbacks to important compositor events. We interact with hooks using mez.hooks, and are able to hook into events such as ViewMapPre which indicates that a new window is being put on the screen or ViewUnmapPost, indicating a view is being removed from the screen. Here we focus any newly created windows like so, and we see that hooks pass unique function args depending on what the event is related to.

mez.hook.add("ViewMapPre", {
    callback = function(view_id)
        mez.view.set_focused(view_id)
    end
})

Special thank to the talented Phantom for designing our logo