Drawing and visuals
This page explains the compositor-drawn side of evil.
These are the things Lua can draw itself, such as:
- dot grids
- focused outlines
- overlays
- debug visuals
Draw hooks are read-only
This is the most important rule on this page.
Draw hooks are for describing visuals, not changing state.
Available draw hooks:
evil.on.draw_backgroundevil.on.draw_window_overlayevil.on.draw_overlay
Use them to return draw shapes. Do not use them as state-changing hooks.
Drawing helpers available today
evil.draw.rect({...})
Filled rectangle.
evil.draw.stroke_rect({...})
Outlined rectangle.
evil.draw.text({...})
Simple text label. Supports x, y, text, color, space, and optionally font_size.
These are intentionally simple.
Draw spaces
Draw helpers support different spaces.
space = "world"
The rectangle is placed in world coordinates. This means it moves with the canvas and windows.
Good for:
- focused borders around windows
- world-space overlays
space = "screen"
The rectangle is placed in screen coordinates. This means it stays fixed relative to the output being drawn.
Good for:
- HUD-like overlays
- dot grid points already converted into screen positions
Typical background example
A background hook often returns many simple rectangles.
The shared example helper common.draw_background uses a dot grid.
Conceptually it:
- looks at the current visible world
- chooses a spacing based on zoom
- converts world positions into screen positions
- returns rectangles for visible dots
That is a good example of a draw hook doing pure visual work.
Typical focused border example
A focused border hook usually:
- finds the focused window from the state snapshot
- returns one or more
stroke_rectcommands around that window
Example pattern:
evil.on.draw_window_overlay = function(ctx)
local focused = ctx.focused_window
if not focused then
return {}
end
return {
evil.draw.stroke_rect({
space = "world",
x = focused.x,
y = focused.y,
w = focused.w,
h = focused.h,
width = 2,
outer = 2,
color = { 0.8, 0.6, 1.0, 1.0 },
}),
}
end
The draw stack
The draw stack is configured in evil.config({...}):
draw = {
stack = { "background", "windows", "window_overlay", "popups", "overlay", "cursor" },
clear_color = { 0.08, 0.05, 0.12, 1.0 },
}
What the layers mean
background— compositor-drawn backgroundwindows— base window surfaceswindow_overlay— compositor visuals around windowspopups— popup/dialog surfacesoverlay— top-level compositor overlay layercursor— compositor cursor layer if used by the backend
The stack is bottom-to-top.
clear_color
This is the background color used before the compositor draws the layers.
Example:
clear_color = { 0.08, 0.05, 0.12, 1.0 }
These values are normalized RGBA values in the range 0..1.
Good visual patterns for users
Focus border
A very good first visual customization.
Dot grid / subtle background
Good for a canvas-oriented environment because it helps communicate world space without much code.
Debug overlays
If you are experimenting, draw hooks are a nice place to draw lightweight debugging shapes.
Things to avoid
- changing state from a draw hook
- overloading a draw hook with lots of unrelated policy
- burying all draw math in one giant function if it can be split into small helpers
Read next
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