Table of Contents
- Lua API guide
- The three main jobs of the Lua API
- The root evil table
- Config-time APIs
- Runtime query helpers
- evil.state()
- evil.pointer.position()
- evil.output.list()
- evil.output.get(id)
- evil.output.primary()
- evil.output.at_pointer()
- evil.window.list()
- evil.window.get(id)
- evil.window.focused()
- evil.canvas.viewport()
- Runtime commands
- Window commands
- evil.window.focus(id)
- evil.window.clear_focus()
- evil.window.move(id, x, y)
- evil.window.resize(id, w, h)
- evil.window.set_bounds(id, x, y, w, h)
- evil.window.begin_move(id)
- evil.window.begin_resize(id, edges)
- evil.window.close(id)
- Canvas commands
- evil.canvas.pan(dx, dy)
- evil.canvas.zoom(factor)
- Draw helpers
- The evil.on hook table
- Common context fields
- ctx.state
- ctx.window
- ctx.window_id
- ctx.pointer
- ctx.modifiers
- ctx.dx, ctx.dy
- ctx.edges
- ctx.button, ctx.button_name, ctx.button_info
- Returning actions from hooks
- Imperative command examples
- Declarative action examples
- Failure behavior to expect
- Good next pages
Lua API guide
This page explains the evil Lua API in a detailed but beginner-friendly way.
If you are completely new to Lua, you should read Lua basics for evil users first.
The three main jobs of the Lua API
The Lua API is mainly used for three things:
- configuration
- setting backend, canvas, draw, window, placement, tty, and rule options
- querying runtime state
- reading windows, outputs, pointer position, and viewport state
- defining policy
- hooks, returned actions, and direct runtime commands
The root evil table
Everything starts from the global evil table.
Common things on it are:
evil.config(...)evil.bind(...)evil.autostart(...)evil.state()evil.window.*evil.output.*evil.pointer.*evil.canvas.*evil.draw.*evil.on.*
Config-time APIs
These are used while loading the config.
evil.config(table)
Use this to define the main configuration.
evil.config({
backend = "winit",
canvas = {
min_zoom = 0.2,
max_zoom = 4.0,
},
})
evil.bind(keyspec, action, opts?)
Use this to add key bindings.
evil.bind("Super+Return", "spawn", { command = "foot" })
evil.autostart(command)
Use this to run commands at compositor startup.
evil.autostart("foot")
Runtime query helpers
These functions let Lua read runtime state.
evil.state()
Returns a snapshot of the runtime.
Main fields:
focused_window_idpointeroutputswindows
Example:
local state = evil.state()
print(state.focused_window_id)
print(#state.windows)
evil.pointer.position()
Returns the pointer position.
Example:
local pointer = evil.pointer.position()
print(pointer.x, pointer.y)
evil.output.list()
Returns every visible output snapshot.
evil.output.get(id)
Returns one output by ID, or nil if missing.
evil.output.primary()
Returns the primary output, or nil if there is none.
evil.output.at_pointer()
Returns the output under the pointer, or nil if none matches.
evil.window.list()
Returns every mapped window snapshot.
evil.window.get(id)
Returns one window snapshot, or nil if it does not exist.
evil.window.focused()
Returns the focused window snapshot, or nil if there is no focused window.
evil.canvas.viewport()
Returns the current viewport snapshot.
Fields include:
xyworld_xworld_yzoomscreen_wscreen_hvisible_world
Runtime commands
These change state directly.
Window commands
evil.window.focus(id)
Focus a window by ID.
Returns:
trueon successfalseif the target is invalid
evil.window.clear_focus()
Clear the current focus.
evil.window.move(id, x, y)
Move a window to a world-space position.
evil.window.resize(id, w, h)
Resize a window.
evil.window.set_bounds(id, x, y, w, h)
Set both position and size together.
evil.window.begin_move(id)
Start interactive move for a window.
evil.window.begin_resize(id, edges)
Start interactive resize.
Example:
evil.window.begin_resize(id, {
left = false,
right = true,
top = false,
bottom = true,
})
evil.window.close(id)
Request that a window close.
Canvas commands
evil.canvas.pan(dx, dy)
Pan the camera by a delta.
evil.canvas.zoom(factor)
Zoom by a factor.
Example:
evil.canvas.zoom(1.15)
Draw helpers
Draw helpers are used only in draw hooks.
evil.draw.rect({...})
Create a filled rectangle draw command.
evil.draw.stroke_rect({...})
Create an outlined rectangle draw command.
Example:
evil.draw.stroke_rect({
space = "world",
x = 100,
y = 100,
w = 300,
h = 200,
width = 2,
outer = 2,
color = { 0.8, 0.6, 1.0, 1.0 },
})
The evil.on hook table
Hooks are assigned as functions.
Example:
evil.on.window_mapped = function(ctx)
evil.window.focus(ctx.window.id)
end
Available hooks today:
evil.on.resolve_focusevil.on.place_windowevil.on.window_mappedevil.on.window_unmappedevil.on.focus_changedevil.on.move_beginevil.on.move_updateevil.on.move_endevil.on.resize_beginevil.on.resize_updateevil.on.resize_endevil.on.keyevil.on.gestureevil.on.draw_backgroundevil.on.draw_window_overlayevil.on.draw_overlay
Each hook gets a ctx table.
Different hooks expose different fields.
For a deeper walkthrough, read Hooks guide.
Common context fields
ctx.state
Runtime snapshot.
ctx.window
Current window snapshot when relevant.
ctx.window_id
Current window ID when relevant.
ctx.pointer
Pointer table when relevant.
Often includes:
xyoutput_idlocal_xlocal_y
ctx.modifiers
Modifier table.
Includes:
ctrlaltshiftsuperlogonamescountanynone
ctx.dx, ctx.dy
Movement delta values.
ctx.edges
Resize edge flags.
ctx.button, ctx.button_name, ctx.button_info
Useful in resolve_focus for pointer-button policy.
Returning actions from hooks
The current shipped examples prefer imperative commands first, but hooks can also return declarative actions when that makes the policy clearer.
Hooks can return:
nil- one action table
{ actions = { ... } }
One action
return {
kind = "focus_window",
id = ctx.window.id,
}
Several actions
return {
actions = {
{ kind = "focus_window", id = ctx.window.id },
{ kind = "begin_move", id = ctx.window.id },
}
}
Supported action kinds today
move_windowresize_windowset_boundsbegin_movebegin_resizefocus_windowclear_focusclose_windowspawnpan_canvaszoom_canvas
For more detail, read Queries, commands, and actions.
Imperative command examples
evil.window.move(ctx.window.id, ctx.window.x + ctx.dx, ctx.window.y + ctx.dy)
evil.window.set_bounds(ctx.window.id, 100, 100, 800, 500)
evil.spawn("foot")
evil.window.focus(ctx.window.id)
Declarative action examples
return {
kind = "move_window",
id = ctx.window.id,
x = ctx.window.x + ctx.dx,
y = ctx.window.y + ctx.dy,
}
return {
kind = "set_bounds",
id = ctx.window.id,
x = 100, y = 100, w = 800, h = 500,
}
return {
kind = "spawn",
command = "foot",
}
return {
kind = "focus_window",
id = ctx.window.id,
}
Failure behavior to expect
The API is intentionally simple.
Queries
Specific-object queries usually return nil when the object is missing.
Examples:
evil.window.get(id)evil.output.get(id)evil.output.primary()when no outputs existevil.output.at_pointer()when no output matches
Commands
Commands usually return:
trueon successfalseif the operation is invalid or rejected
Hooks
Hooks should return:
nil- one action table
- or
{ actions = { ... } }
Returning the wrong shape causes a validation error.
Good next pages
evil wiki
- Home
- Getting started
- Architecture and philosophy
- Configuration guide
- Writing your first config
- Lua basics for evil users
- Lua API guide
- Lua API cheat sheet
- Hooks guide
- Hook payload summary
- Queries, commands, and actions
- Drawing and visuals
- Example configs overview
- Example shared helpers and rules
- IPC protocol
- Testing and debugging
- Contributor validation matrix
- Feature status and limitations
- Feature and support matrix
- Recipes