[PR #497] [MERGED] feat: new cairo module #7785

Closed
opened 2026-05-23 02:55:47 +01:00 by JakeStanger · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/JakeStanger/ironbar/pull/497
Author: @JakeStanger
Created: 3/14/2024
Status: Merged
Merged: 4/17/2024
Merged by: @JakeStanger

Base: masterHead: feat/cairo


📝 Commits (4)

  • 7b08949 build: remove some unused features/deps
  • b0a05b7 feat: new cairo module
  • 653d1d6 ci(setup): add luajit dep
  • a62010e build(nix): add luajit and lgi support

📊 Changes

17 files changed (+621 additions, -23 deletions)

View changed files

📝 .github/scripts/ubuntu_setup.sh (+2 -1)
📝 Cargo.lock (+54 -2)
📝 Cargo.toml (+8 -0)
📝 docs/Compiling.md (+3 -2)
📝 docs/_Sidebar.md (+1 -0)
docs/modules/Cairo.md (+215 -0)
📝 flake.nix (+6 -0)
lua/draw.lua (+4 -0)
lua/init.lua (+4 -0)
📝 nix/default.nix (+26 -3)
📝 shell.nix (+2 -0)
src/clients/lua.rs (+41 -0)
📝 src/clients/mod.rs (+13 -0)
📝 src/config/mod.rs (+6 -0)
📝 src/main.rs (+36 -15)
src/modules/cairo.rs (+198 -0)
📝 src/modules/mod.rs (+2 -0)

📄 Description

This is an early working draft of the Cairo module, which allows you to write custom Lua scripts to draw whatever you like to a region of the bar. It's pretty rough around the edges but just about good enough to start playing with.

Currently this cannot be used in popups, however that will change fairly soon once #131 is resolved (that's been brought forward specifically because of this).

You need LuaJIT installed along with the LGI package (lua-lgi on Arch). This needs to be installed to the LUA_PATH and LUA_CPATH respectively. If installing system-wide, this should Just Werk as it's in /usr.

I have played around with the idea of vendoring but suspect I will need to write bindings to statically link lua-lgi and its scripts into the Ironbar binary, so unless anybody has any better ideas we'll roll with it like this for now.

Example usage

In your config you can define eg:

let {
  $cairo = { type = "cairo" path = "$config_dir/clock.lua" frequency = 50 width = 300 height = 300 }
} in {
  center = [ $cairo ]
}

Where:

  • path - path to Lua script
  • interval number of milliseconds between updates
  • width - canvas width
  • height - canvas height

GTK's sizing rules mean if the canvas height (or width for vertical bars) is less than the bar height, it will automatically expand to the bar height.

At the specified path, define a Lua script. The script must contain a function called draw, which takes the Cairo context as a parameter. Other than that, you have free reign.

You can optionally create an init.lua file in your config directory, which is executed once on bar startup. This can house any required initialization code, as well as shared functions.

The most basic example, which draws a red square, can be shown below:

function draw(cr) 
    cr:set_source_rgb(1.0, 0.0, 0.0)
    cr:paint()
end

A more complex example is included on the wiki page.

OLD Currently there are some hard rules you need to keep in mind:
  • The script can contain a single function only. This does not need a name.
  • The function takes a single argument, which is a pointer to the Cairo context. You must pass this to the context constructor using eg local cr = cairo.Context(ptr) to get a usable object.
  • The function must return an unsigned integer. This is not used, so you can return 0.
  • The cairo global is available everywhere. This gives full access to the Cairo library, although some constructors may not work yet.

I've included a proof-of-concept example script below, which results in a kinda nifty clock I wrote for Conky about 5 years ago.

OLD Lua script
function(ptr)
    local cr = cairo.Context(ptr)

    local center_x = 150
    local center_y = 150
    local radius = 130

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

    local hours = date_table["hour"]
    local minutes = date_table["min"]
    local seconds = date_table["sec"]

    local ms = tostring(io.popen('date +%s%3N'):read('a')):sub(-4, 9999)
    ms = tonumber(ms) / 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

image

I want to try and polish this up and sort most of those gotchas out before this gets merged. Any and all help/ideas welcome.

Resolves #105


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/JakeStanger/ironbar/pull/497 **Author:** [@JakeStanger](https://github.com/JakeStanger) **Created:** 3/14/2024 **Status:** ✅ Merged **Merged:** 4/17/2024 **Merged by:** [@JakeStanger](https://github.com/JakeStanger) **Base:** `master` ← **Head:** `feat/cairo` --- ### 📝 Commits (4) - [`7b08949`](https://github.com/JakeStanger/ironbar/commit/7b089495ad7593b56ce8082108b1e22a9b03cb89) build: remove some unused features/deps - [`b0a05b7`](https://github.com/JakeStanger/ironbar/commit/b0a05b7cda1d07af6673a5ee9fb8105ed1497a36) feat: new cairo module - [`653d1d6`](https://github.com/JakeStanger/ironbar/commit/653d1d6aed28241f5699759352bdc118a8e176dd) ci(setup): add luajit dep - [`a62010e`](https://github.com/JakeStanger/ironbar/commit/a62010ee4fd25c620df726970b070f63aa9f45cc) build(nix): add luajit and lgi support ### 📊 Changes **17 files changed** (+621 additions, -23 deletions) <details> <summary>View changed files</summary> 📝 `.github/scripts/ubuntu_setup.sh` (+2 -1) 📝 `Cargo.lock` (+54 -2) 📝 `Cargo.toml` (+8 -0) 📝 `docs/Compiling.md` (+3 -2) 📝 `docs/_Sidebar.md` (+1 -0) ➕ `docs/modules/Cairo.md` (+215 -0) 📝 `flake.nix` (+6 -0) ➕ `lua/draw.lua` (+4 -0) ➕ `lua/init.lua` (+4 -0) 📝 `nix/default.nix` (+26 -3) 📝 `shell.nix` (+2 -0) ➕ `src/clients/lua.rs` (+41 -0) 📝 `src/clients/mod.rs` (+13 -0) 📝 `src/config/mod.rs` (+6 -0) 📝 `src/main.rs` (+36 -15) ➕ `src/modules/cairo.rs` (+198 -0) 📝 `src/modules/mod.rs` (+2 -0) </details> ### 📄 Description This is an early working draft of the Cairo module, which allows you to write custom Lua scripts to draw whatever you like to a region of the bar. It's pretty rough around the edges but just about good enough to start playing with. Currently this cannot be used in popups, however that will change fairly soon once #131 is resolved (that's been brought forward specifically because of this). You need LuaJIT installed along with the [LGI](https://github.com/lgi-devs/lgi) package (`lua-lgi` on Arch). This needs to be installed to the `LUA_PATH` and `LUA_CPATH` respectively. If installing system-wide, this should Just Werk as it's in `/usr`. I have played around with the idea of vendoring but suspect I will need to write bindings to statically link `lua-lgi` and its scripts into the Ironbar binary, so unless anybody has any better ideas we'll roll with it like this for now. ## Example usage In your config you can define eg: ```corn let { $cairo = { type = "cairo" path = "$config_dir/clock.lua" frequency = 50 width = 300 height = 300 } } in { center = [ $cairo ] } ``` Where: - `path` - path to Lua script - `interval` number of milliseconds between updates - `width` - canvas width - `height` - canvas height GTK's sizing rules mean if the canvas height (or width for vertical bars) is less than the bar height, it will automatically expand to the bar height. At the specified path, define a Lua script. The script must contain a function called `draw`, which takes the Cairo context as a parameter. Other than that, you have free reign. You can optionally create an `init.lua` file in your config directory, which is executed once on bar startup. This can house any required initialization code, as well as shared functions. The most basic example, which draws a red square, can be shown below: ```lua function draw(cr) cr:set_source_rgb(1.0, 0.0, 0.0) cr:paint() end ``` A more complex example is included on the wiki page. <details> <summary>OLD</summary> Currently there are some hard rules you need to keep in mind: - The script can contain a **single** function only. This does not need a name. - The function takes a single argument, which is a pointer to the Cairo context. You must pass this to the context constructor using eg `local cr = cairo.Context(ptr)` to get a usable object. - The function must return an unsigned integer. This is not used, so you can return `0`. - The `cairo` global is available everywhere. This gives full access to the Cairo library, although some constructors may not work yet. I've included a proof-of-concept example script below, which results in a kinda nifty clock I wrote for Conky about 5 years ago. <details><summary>OLD Lua script</summary> ```lua function(ptr) local cr = cairo.Context(ptr) local center_x = 150 local center_y = 150 local radius = 130 local date_table = os.date("*t") local hours = date_table["hour"] local minutes = date_table["min"] local seconds = date_table["sec"] local ms = tostring(io.popen('date +%s%3N'):read('a')):sub(-4, 9999) ms = tonumber(ms) / 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 ``` </details> </details> ![image](https://github.com/JakeStanger/ironbar/assets/5057870/05d20406-937b-4bfc-b253-5aaf73308485) I want to try and polish this up and sort most of those gotchas out before this gets merged. Any and all help/ideas welcome. Resolves #105 --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
JakeStanger 2026-05-23 02:55:47 +01:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
JakeStanger/ironbar#7785
No description provided.