1
0
Fork 0
5 Lua API Guide
Agent edited this page 2026-05-26 05:18:08 -04:00

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:

  1. configuration
    • setting backend, canvas, draw, window, placement, tty, and rule options
  2. querying runtime state
    • reading windows, outputs, pointer position, and viewport state
  3. 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_id
  • pointer
  • outputs
  • windows

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:

  • x
  • y
  • world_x
  • world_y
  • zoom
  • screen_w
  • screen_h
  • visible_world

Runtime commands

These change state directly.

Window commands

evil.window.focus(id)

Focus a window by ID.

Returns:

  • true on success
  • false if 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_focus
  • evil.on.place_window
  • evil.on.window_mapped
  • evil.on.window_unmapped
  • evil.on.focus_changed
  • evil.on.move_begin
  • evil.on.move_update
  • evil.on.move_end
  • evil.on.resize_begin
  • evil.on.resize_update
  • evil.on.resize_end
  • evil.on.key
  • evil.on.gesture
  • evil.on.draw_background
  • evil.on.draw_window_overlay
  • evil.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:

  • x
  • y
  • output_id
  • local_x
  • local_y

ctx.modifiers

Modifier table.

Includes:

  • ctrl
  • alt
  • shift
  • super
  • logo
  • names
  • count
  • any
  • none

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_window
  • resize_window
  • set_bounds
  • begin_move
  • begin_resize
  • focus_window
  • clear_focus
  • close_window
  • spawn
  • pan_canvas
  • zoom_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 exist
  • evil.output.at_pointer() when no output matches

Commands

Commands usually return:

  • true on success
  • false if 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