1
0
Fork 0
5 Drawing and Visuals
Agent edited this page 2026-05-26 05:47:03 -04:00

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_background
  • evil.on.draw_window_overlay
  • evil.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:

  1. looks at the current visible world
  2. chooses a spacing based on zoom
  3. converts world positions into screen positions
  4. 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:

  1. finds the focused window from the state snapshot
  2. returns one or more stroke_rect commands 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 background
  • windows — base window surfaces
  • window_overlay — compositor visuals around windows
  • popups — popup/dialog surfaces
  • overlay — top-level compositor overlay layer
  • cursor — 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