Skip to content

Plugins

SamWM has a Lua-based plugin system that allows extending the window manager with custom behavior.

Plugin System

Plugins are Lua scripts that register event handlers, custom layouts, window rules, and actions.

Loading Plugins

Place plugin files in ~/.config/samwm/plugins/ and load them from your config:

dofile(os.getenv("HOME") .. "/.config/samwm/plugins/my-plugin.lua")
``continue`

### Plugin Structure

```lua
samwm.plugin("my-plugin", function()
    samwm.log("My plugin initialized!")
end)

Events

Register handlers for compositor events:

samwm.on_event("window_created", function(data)
    local info = json.decode(data)
    samwm.log("New window: " .. info.title .. " (" .. info.class .. ")")
end)

samwm.on_event("window_destroyed", function(data)
    samwm.log("Window closed")
end)

samwm.on_event("focus_changed", function(data)
    samwm.log("Focus changed")
end)

samwm.on_event("workspace_changed", function(data)
    samwm.log("Workspace: " .. data)
end)

samwm.on_event("layout_changed", function(data)
    samwm.log("Layout: " .. data)
end)

Window Rules

Apply rules based on window class/title:

samwm.set_window_rule({ class = "firefox" }, "workspace 2")
samwm.set_window_rule({ title = "Picture-in-Picture" }, "float")
samwm.set_window_rule({ class = "mpv" }, "fullscreen")

Rule actions:

  • "float" — Make window floating
  • "tile" — Force tiling
  • "fullscreen" — Make fullscreen
  • "workspace N" — Move to workspace N

Custom Layouts as Plugins

samwm.plugin("fibonacci-layout", function()
    samwm.register_layout("fibonacci", function(windows, bounds)
        local result = {}
        local remaining = {}
        for _, w in pairs(windows) do
            table.insert(remaining, w)
        end

        local x, y = bounds.x, bounds.y
        local w, h = bounds.width, bounds.height
        local horiz = true

        for i, win in ipairs(remaining) do
            if i == #remaining then
                table.insert(result, {
                    id = win.id, x = x, y = y,
                    width = w, height = h
                })
            elseif horiz then
                local half = math.floor(w / 2)
                table.insert(result, {
                    id = win.id, x = x, y = y,
                    width = half, height = h
                })
                x = x + half
                w = w - half
                horiz = false
            else
                local half = math.floor(h / 2)
                table.insert(result, {
                    id = win.id, x = x, y = y,
                    width = w, height = half
                })
                y = y + half
                h = h - half
                horiz = true
            end
        end

        return result
    end)

    samwm.log("Fibonacci layout registered")
end)

Window Manipulation from Lua

-- Get all windows
local windows = samwm.get_windows()
for _, w in pairs(windows) do
    samwm.log(w.title .. " at (" .. w.x .. "," .. w.y .. ")")
end

-- Get focused window
local focused = samwm.get_focused()
if focused then
    samwm.log("Focused: " .. focused.title)
end

-- Move a window
samwm.move_window(window_id, x, y)

-- Resize a window
samwm.resize_window(window_id, width, height)

-- Get output dimensions
local size = samwm.get_output_size()
samwm.log("Output: " .. size.width .. "x" .. size.height)

Complete Plugin Example

-- ~/.config/samwm/plugins/auto-center-float.lua
samwm.plugin("auto-center-float", function()
    samwm.on_event("window_created", function(data)
        -- Auto-center floating windows
        local size = samwm.get_output_size()
        local focused = samwm.get_focused()
        if focused and focused.floating then
            local wx = math.floor((size.width - focused.width) / 2)
            local wy = math.floor((size.height - focused.height) / 2)
            samwm.move_window(focused.id, wx, wy)
        end
    end)
end)

Lua API Reference

Function Description
samwm.bind(mod, key, action) Register keybinding
samwm.exec(cmd) Execute shell command
samwm.focus(dir) Focus next/prev window
samwm.toggle_floating() Toggle focused window floating
samwm.toggle_fullscreen() Toggle focused window fullscreen
samwm.close_window() Close focused window
samwm.switch_layout(name) Switch active layout
samwm.list_layouts() List available layouts
samwm.register_layout(name, fn) Register custom layout
samwm.get_windows() Get all windows
samwm.get_focused() Get focused window
samwm.move_window(id, x, y) Move window
samwm.resize_window(id, w, h) Resize window
samwm.set_window_rule(match, action) Set window rule
samwm.on_event(event, fn) Register event handler
samwm.plugin(name, fn) Register plugin
samwm.get_output_size() Get output dimensions
samwm.move_to_workspace(n) Move focused window to workspace
samwm.set_workspace(n) Switch to workspace
samwm.send_action(action) Send custom action
samwm.log(msg) Log message