Skip to content
Jake Stanger edited this page Jan 29, 2026 · 3 revisions

Allows you to render custom content using the Lua and the Cairo drawing library. This is an advanced feature which provides a powerful escape hatch, allowing you to fetch data and render anything using an embedded scripting environment.

Scripts are automatically hot-reloaded.

Note

The Lua engine uses LuaJIT 5.1, and requires the use of a library called lgi. Ensure you have the correct lua-lgi package installed.

Circle clock

Configuration

Type: cairo

Name Type Default Description
path string null The path to the Lua script to load.
frequency float 200 The number of milliseconds between each draw call.
width integer 42 The canvas width in pixels.
height integer 42 The canvas height in pixels.
JSON
{
  "center": [
    {
      "type": "cairo",
      "path": ".config/ironbar/clock.lua",
      "frequency": 100,
      "width": 300,
      "height": 300
    }
  ]
}
TOML
[[center]]
type = "cairo"
path = ".config/ironbar/clock.lua"
frequency = 100
width = 300
height = 300
YAML
center:
- type: cairo
  path: .config/ironbar/clock.lua
  frequency: 100
  width: 300
  height: 300
Corn
let { 
    $config_dir = ".config/ironbar" 
    $cairo = { 
        type = "cairo" 
        path = "$config_dir/clock.lua" 
        frequency = 100 
        width = 300 
        height = 300 
    } 
} in { 
    center = [ $cairo ] 
}

Script

Every script must return a function or callable table with three parameters:

  • Cairo context (required)
  • Width of the drawing area (can be omitted)
  • Height of the drawing area (can be omitted)

Outside of this, you can do whatever you like. The full lua stdlib is available, and you can load in additional system packages as desired.

Additionally there is basic access to the ironbar via the ironbar global:

  • ironbar.config_dir: Absolute path to the configuration directory. This can be used for relative file imports, e.g.:
     local_module = dofile(ironbar.config_dir .. "local_mod.lua")`
  • ironbar:log_debug(msg), ironbar:log_info(msg), ironbar:log_warn(msg), ironbar:log_error(msg): Write a log message.
  • ironbar:unixtime(): Returns high-resolution unixtime (stdlib only offers second-resolution). Will return a table:
    • secs: Seconds since unix-epoch with fractions
    • subsec_millis: Sub-second milliseconds as integer
    • subsec_micros: Sub-second microseconds as integer
  • ironbar:var_get(key): Get an ironbar variable, e.g.
    memory_free = ironbar:var_get("sysinfo.memory_free")
  • ironbar:var_list(namespace): Get all variables of a namespace as table (non-recursive), e.g.:
    memory_free = ironbar:var_list("sysinfo")["memory_free"]

Basic examples, which draw a red square

  • As anonymous function:
    return function(cr) 
      cr:set_source_rgb(1.0, 0.0, 0.0)
      cr:paint()
    end
  • As local function:
    local function draw_square(cr) 
      cr:set_source_rgb(1.0, 0.0, 0.0)
      cr:paint()
    end
    
    return draw_square
  • As callable table (also using width and height):
    local square = {}
    
    function square:draw(cr, width, height)
      cr:set_source_rgb(1.0, 0.0, 0.0)
      cr:rectangle(0, 0, width, height)
      cr:fill()
    end
    
    setmetatable(square, {
     __call = function(o, cr, width, height)
        return o:draw(cr, width, height)
     end,
    })
    
    return square

A longer example, used to create the clock in the image at the top of the page, is shown below:

Circle clock
local function draw_clock(cr, width, height)
    local center_x = width / 2
    local center_y = height / 2
    local radius = math.min(width, height) / 2 - 20

    local date_table = os.date("*t")

    local hours = date_table["hour"]
    local minutes = date_table["min"]
    local seconds = date_table["sec"]
    local ms = ironbar:unixtime().subsec_millis / 1000


    local label_seconds = seconds
    seconds = seconds + ms

    local hours_str = tostring(hours)
    if string.len(hours_str) == 1 then
        hours_str = "0" .. hours_str
    end

    local minutes_str = tostring(minutes)
    if string.len(minutes_str) == 1 then
        minutes_str = "0" .. minutes_str
    end

    local seconds_str = tostring(label_seconds)
    if string.len(seconds_str) == 1 then
        seconds_str = "0" .. seconds_str
    end

    local font_size = radius / 5.5

    cr:set_source_rgb(1.0, 1.0, 1.0)

    cr:move_to(center_x - font_size * 2.5 + 10, center_y + font_size / 2.5)
    cr:set_font_size(font_size)
    cr:show_text(hours_str .. ':' .. minutes_str .. ':' .. seconds_str)
    cr:stroke()

    if hours > 12 then
        hours = hours - 12
    end

    local line_width = radius / 8
    local start_angle = -math.pi / 2

    local end_angle = start_angle + ((hours + minutes / 60 + seconds / 3600) / 12) * 2 * math.pi
    cr:set_line_width(line_width)
    cr:arc(center_x, center_y, radius, start_angle, end_angle)
    cr:stroke()

    end_angle = start_angle + ((minutes + seconds / 60) / 60) * 2 * math.pi
    cr:set_line_width(line_width)
    cr:arc(center_x, center_y, radius * 0.8, start_angle, end_angle)
    cr:stroke()

    if seconds == 0 then
        seconds = 60
    end

    end_angle = start_angle + (seconds / 60) * 2 * math.pi
    cr:set_line_width(line_width)
    cr:arc(center_x, center_y, radius * 0.6, start_angle, end_angle)
    cr:stroke()

    return 0
end

return draw_clock

Tip

The C documentation for the Cairo context interface can be found here. The Lua interface provides a slightly friendlier API which restructures things slightly. The cairo_ prefix is dropped, and the cairo_t *cr parameters are replaced with a namespaced call. For example, cairo_paint (cairo_t *cr) becomes cr:paint()

Tip

Ironbar's Cairo module has similar functionality to the popular Conky program. You can often re-use scripts with little work.

Initialization

You can optionally create an init.lua file in your config directory. Any code in here will be executed once, on bar startup.

As variables and functions are global by default in Lua, this provides a mechanism for sharing code between multiple modules.

Styling

Selector Description
.cairo Cairo widget container.

For more information on styling, please see the styling guide.

Clone this wiki locally