LACCC – Locale‑Aware Conky Clock & Calendar (2026 Edition)

LACCC – Locale‑Aware Conky Clock & Calendar (2026 Edition)

A modular, Lua‑powered Conky framework for Wayland and X11

I’m sharing my custom Conky project called LACCC (Locale‑Aware Conky Clock & Calendar). It’s a modern, modular, Lua‑driven Conky setup designed to work reliably across all major desktop environments, including Wayland sessions.

The goal was to build a clean, transparent, locale‑aware clock and calendar widget that:

  • starts reliably on KDE, GNOME, XFCE, Cinnamon, MATE, LXQt, and others

  • works on both Wayland and X11

  • uses a modern, minimal visual style

  • is fully modular through Lua

  • is easy to extend with additional widgets

  • follows a consistent 2026 branding and structure

:package: Project structure

All files live in a single directory:

Code

~/.conky/calendar_clock/
 ├─ lacc.conf
 ├─ main.lua
 ├─ autostart.sh
 ├─ lua/
 │   ├─ clock.lua
 │   ├─ calendar.lua
 │   ├─ background.lua
 │   └─ text.lua

The main Conky config loads the Lua modules using relative paths:

lua

lua_load = './main.lua'
lua_draw_hook_post = 'main'

This works because Conky is launched from inside the directory, so ./lua/ always resolves correctly.

:rocket: Autostart – reliable startup on all DEs

Many desktop environments initialize their compositor and ARGB layers after the session technically starts. If Conky launches too early, transparency or layering may fail.

This autostart script ensures stable startup:

bash

#!/bin/bash
​
cd "$HOME/.conky/calendar_clock" || exit
​
sleep 1
​
setsid conky -c laccc.conf &
​
sleep 1
exit

This approach:

  • guarantees Conky starts from the correct working directory

  • ensures all relative Lua paths resolve properly

  • avoids early startup issues by adding a short delay

  • works consistently across all DEs

:artist_palette: Visual style

The widget uses:

  • a clean, transparent layout

  • modern typography

  • locale‑aware date formatting

  • Lua‑controlled background, shadow, and text rendering

  • a modular drawing pipeline

The system is easy to extend with:

  • weather widgets

  • system monitors

  • network indicators

  • GPU/CPU stats

  • any custom Lua module you want to add

:puzzle_piece: Lua modules

Each component lives in its own file inside lua/:

  • main.lua — drawing pipeline and update loop

  • --[[
    ===============================================================================
    Locale‑Aware Conky Clock & Calendar (LACCC)
    MAIN MODULE – Loader and dispatcher for all LACCC components
    Part of the 2026 ConkySystem Framework
    ===============================================================================
    ​
    OVERVIEW
    --------
    This module:
    ​
    • detects the script directory,
    • extends Lua's module search path,
    • loads all LACCC submodules (background, text, clock, calendar),
    • provides a unified Conky entry point,
    • ensures consistent rendering order across components.
    ​
    ===============================================================================
    ]] --
    ​
    -- Detect script directory
    local info = debug.getinfo(1, "S").source
    local script_path = info:match("@(.*/)")
    if not script_path then script_path = "./" end
    -- Extend Lua module search path
    package.path =
        script_path .. "lua/?.lua;" ..
        package.path
    require 'background'
    require 'text'
    require 'clock'
    require 'calendar'
    function conky_main()
    if conky_main_text then conky_main_text() end
        if conky_main_background then conky_main_background() end
        if conky_main_clock then conky_main_clock() end
        if conky_main_calendar then conky_main_calendar() end
    end
    ​
    
  • clock.lua — clock rendering

  • --[[
    ===============================================================================
    Locale‑Aware Conky Clock & Calendar (LACCC)
    ANALOG CLOCK MODULE – Gradient dial, ticks, hands using Cairo
    Part of the 2026 ConkySystem Framework
    ===============================================================================
    ​
    OVERVIEW
    --------
    This module renders a fully configurable analog clock using Cairo.
    It provides:
    ​
    • gradient dial and gradient border,
    • hour/minute/second hands with independent styles,
    • hour and minute ticks,
    • optional 1–12 numbers,
    • accurate system‑time based movement,
    • separate gradient profiles for every visual element,
    • Cairo fallback when cairo_xlib is unavailable.
    ​
    DRAW CONDITION
    --------------
    draw_me
    Controls whether the clock is drawn.
    • true / false
    • Conky condition string
    • Lua function returning boolean
    ​
    COLOR HANDLING
    --------------
    hex_to_rgba(hex, alpha)
    Converts 0xRRGGBB → Cairo RGBA.
    ​
    get_color_from_list(stops, t)
    Interpolates multi‑stop gradients (0–1 range).
    ​
    ===============================================================================
    ]] --
    ​
    require 'cairo'
    ​
    ------------------------------------------------------------
    -- CAIRO FALLBACK
    ------------------------------------------------------------
    local ok, cairo_xlib = pcall(require, 'cairo_xlib')
    if not ok then
        cairo_xlib = setmetatable({}, {
            __index = function(_, k) return _G[k] end
        })
    end
    clock = {
        draw_me = true,
    ​
        x = 145,
        y = 145,
        radius = 130,
    ​
        show_ticks = true,
        show_numbers = true,
        show_seconds = true,
    ​
        tick_width_hour = 3,
        tick_width_minute = 1,
    ​
        number_size = 18,
        number_radius = 0.78,
    ​
        hour_hand_width = 6,
        minute_hand_width = 4,
        second_hand_width = 2,
    ​
        center_radius = 4,
    ​
        bg = {
            { 1, 0xfcfcfc, 0.03 },
        },
    ​
        border = {
            { 1, 0x4c4e51, 1 },
        },
    ​
        tick_color = {
            { 1, 0xfcfcfc, 1 },
        },
    ​
        number_color = {
            { 1, 0xfcfcfc, 1 },
        },
    ​
        hour_color = {
            { 1, 0xfcfcfc, 1 },
        },
    ​
        minute_color = {
            { 1, 0xfcfcfc, 1 },
        },
    ​
        second_color = {
            { 1, 0x3daee9, 1 },
        },
    ​
        center_color = {
            { 1, 0xfcfcfc, 1 },
        },
    }
    ------------------------------------------------------------
    -- UTILS
    ------------------------------------------------------------
    function hex_to_rgba(hex, alpha)
        if type(hex) == "string" then
            hex = tonumber(hex:gsub("#", ""), 16)
        end
        local r = ((hex >> 16) & 0xFF) / 255
        local g = ((hex >> 8) & 0xFF) / 255
        local b = (hex & 0xFF) / 255
        return r, g, b, alpha
    end
    ​
    function draw_allowed(flag)
        if flag == nil or flag == true then return true end
        return conky_parse(tostring(flag)) == "1"
    end
    ​
    function get_color_from_list(stops, t)
        local prev = stops[1]
        for i = 2, #stops do
            local nxt = stops[i]
            if t <= nxt[1] then
                local p = (t - prev[1]) / (nxt[1] - prev[1])
                local r1, g1, b1, a1 = hex_to_rgba(prev[2], prev[3])
                local r2, g2, b2, a2 = hex_to_rgba(nxt[2], nxt[3])
                return
                    r1 + (r2 - r1) * p,
                    g1 + (g2 - g1) * p,
                    b1 + (b2 - b1) * p,
                    a1 + (a2 - a1) * p
            end
            prev = nxt
        end
        return hex_to_rgba(prev[2], prev[3])
    end
    ​
    ------------------------------------------------------------
    -- DEFAULT CLOCK CONFIG
    ------------------------------------------------------------
    CLOCK_DEFAULT = {
        draw_me = true,
    ​
        x = 200,
        y = 200,
        radius = 80,
    ​
        show_ticks = true,
        show_numbers = true,
        show_seconds = true,
    ​
        tick_width_hour = 3,
        tick_width_minute = 1,
    ​
        number_size = 18,
        number_radius = 0.78,
    ​
        hour_hand_width = 5,
        minute_hand_width = 3,
        second_hand_width = 1,
    ​
        center_radius = 4,
    ​
        bg = {
            { 0, 0x222222, 0.8 },
            { 1, 0x222222, 0.8 },
        },
    ​
        border = {
            { 0, 0xffffff, 0.4 },
            { 1, 0xffffff, 0.4 },
        },
    ​
        tick_color = {
            { 0, 0xffffff, 1 },
            { 1, 0xffffff, 1 },
        },
    ​
        number_color = {
            { 0, 0xffffff, 1 },
            { 1, 0xffffff, 1 },
        },
    ​
        hour_color = {
            { 0, 0xffffff, 1 },
            { 1, 0xffffff, 1 },
        },
    ​
        minute_color = {
            { 0, 0xffffff, 1 },
            { 1, 0xffffff, 1 },
        },
    ​
        second_color = {
            { 0, 0xff0000, 1 },
            { 1, 0xff0000, 1 },
        },
    ​
        center_color = {
            { 0, 0xffffff, 1 },
            { 1, 0xffffff, 1 },
        },
    }
    ​
    ------------------------------------------------------------
    -- CLOCK RENDERER
    ------------------------------------------------------------
    function draw_clock(cr, opts)
        if not draw_allowed(opts.draw_me) then return end
        if conky_window == nil then return end
    ​
        -- apply defaults
        local cfg = {}
        for k, v in pairs(CLOCK_DEFAULT) do cfg[k] = v end
        for k, v in pairs(opts) do cfg[k] = v end
    ​
        local x          = cfg.x
        local y          = cfg.y
        local r          = cfg.radius
    ​
        -- time values
        local hours      = tonumber(os.date("%I"))
        local minutes    = tonumber(os.date("%M"))
        local seconds    = tonumber(os.date("%S"))
    ​
        local sec_angle  = (seconds / 60) * 2 * math.pi
        local min_angle  = (minutes / 60) * 2 * math.pi
        local hour_angle = ((hours % 12) / 12 + minutes / 720) * 2 * math.pi
    ​
        --------------------------------------------------------
        -- BACKGROUND (gradient dial)
        --------------------------------------------------------
        for i = 0, 360 do
            local t = i / 360
            local r1, g1, b1, a1 = get_color_from_list(cfg.bg, t)
            cairo_set_source_rgba(cr, r1, g1, b1, a1)
    ​
            local a1 = math.rad(i)
            local a2 = math.rad(i + 1)
    ​
            cairo_move_to(cr, x, y)
            cairo_arc(cr, x, y, r, a1, a2)
            cairo_fill(cr)
        end
    ​
        --------------------------------------------------------
        -- BORDER (gradient ring)
        --------------------------------------------------------
        for i = 0, 360 do
            local t = i / 360
            local r1, g1, b1, a1 = get_color_from_list(cfg.border, t)
            cairo_set_source_rgba(cr, r1, g1, b1, a1)
    ​
            local a1 = math.rad(i) - math.pi / 2
            local a2 = math.rad(i + 1) - math.pi / 2
    ​
            cairo_set_line_width(cr, 2)
            cairo_arc(cr, x, y, r, a1, a2)
            cairo_stroke(cr)
        end
    ​
        --------------------------------------------------------
        -- TICKS
        --------------------------------------------------------
        if cfg.show_ticks then
            for i = 0, 59 do
                local angle          = (i / 60) * 2 * math.pi
                local sin_a          = math.sin(angle)
                local cos_a          = math.cos(angle)
    ​
                local is_hour        = (i % 5 == 0)
                local tick_len       = is_hour and 10 or 5
                local tick_w         = is_hour and cfg.tick_width_hour or cfg.tick_width_minute
    ​
                local t              = i / 60
                local r1, g1, b1, a1 = get_color_from_list(cfg.tick_color, t)
                cairo_set_source_rgba(cr, r1, g1, b1, a1)
                cairo_set_line_width(cr, tick_w)
    ​
                local x1 = x + sin_a * (r - tick_len)
                local y1 = y - cos_a * (r - tick_len)
                local x2 = x + sin_a * r
                local y2 = y - cos_a * r
    ​
                cairo_move_to(cr, x1, y1)
                cairo_line_to(cr, x2, y2)
                cairo_stroke(cr)
            end
        end
    ​
        --------------------------------------------------------
        -- NUMBERS
        --------------------------------------------------------
        if cfg.show_numbers then
            cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD)
            cairo_set_font_size(cr, cfg.number_size)
    ​
            for i = 1, 12 do
                local angle = (i / 12) * 2 * math.pi
                local sin_a = math.sin(angle)
                local cos_a = math.cos(angle)
    ​
                local nx = x + sin_a * (r * cfg.number_radius)
                local ny = y - cos_a * (r * cfg.number_radius)
    ​
                local t = i / 12
                local r1, g1, b1, a1 = get_color_from_list(cfg.number_color, t)
                cairo_set_source_rgba(cr, r1, g1, b1, a1)
    ​
                local text = tostring(i)
                local ext = cairo_text_extents_t:create()
                cairo_text_extents(cr, text, ext)
    ​
                cairo_move_to(cr, nx - ext.width / 2, ny + ext.height / 2)
                cairo_show_text(cr, text)
            end
        end
    ​
        --------------------------------------------------------
        -- HAND DRAWER
        --------------------------------------------------------
        local function draw_hand(angle, length, thickness, color)
            cairo_set_line_width(cr, thickness)
    ​
            local t = angle / (2 * math.pi)
            local r1, g1, b1, a1 = get_color_from_list(color, t)
            cairo_set_source_rgba(cr, r1, g1, b1, a1)
    ​
            local ex = x + math.sin(angle) * length
            local ey = y - math.cos(angle) * length
    ​
            cairo_move_to(cr, x, y)
            cairo_line_to(cr, ex, ey)
            cairo_stroke(cr)
        end
    ​
        draw_hand(hour_angle, r * 0.5, cfg.hour_hand_width, cfg.hour_color)
        draw_hand(min_angle, r * 0.75, cfg.minute_hand_width, cfg.minute_color)
    ​
        if cfg.show_seconds then
            draw_hand(sec_angle, r * 0.9, cfg.second_hand_width, cfg.second_color)
        end
    ​
        --------------------------------------------------------
        -- CENTER DOT
        --------------------------------------------------------
        local r1, g1, b1, a1 = get_color_from_list(cfg.center_color, 0.5)
        cairo_set_source_rgba(cr, r1, g1, b1, a1)
        cairo_arc(cr, x, y, cfg.center_radius, 0, 2 * math.pi)
        cairo_fill(cr)
    end
    ​
    ------------------------------------------------------------
    -- CONKY ENTRY POINT
    ------------------------------------------------------------
    function conky_main_clock()
        if conky_window == nil then return end
        if not clock then return end
    ​
        local cs = cairo_xlib_surface_create(
            conky_window.display,
            conky_window.drawable,
            conky_window.visual,
            conky_window.width,
            conky_window.height
        )
    ​
        local cr = cairo_create(cs)
    ​
        draw_clock(cr, clock)
    ​
        cairo_destroy(cr)
        cairo_surface_destroy(cs)
    end
    ​
    
  • calendar.lua — calendar generation

  • --[[
    ===============================================================================
    Locale‑Aware Conky Clock & Calendar (LACCC)
    CALENDAR MODULE – cal‑based multilingual engine with monthly cache
    Part of the 2026 ConkySystem Framework
    ===============================================================================
    ​
    OVERVIEW
    --------
    This module renders a fully locale‑aware calendar using Cairo.
    It provides:
    ​
    • automatic language detection via system locale,
    • month and weekday names parsed from `cal`,
    • correct week start based on locale (no manual config),
    • monthly caching to avoid repeated external calls,
    • Cairo‑based rendering with gradients and alignment,
    • support for week numbers, outside‑month days, and today highlighting.
    ​
    ===============================================================================
    ]] --
    ​
    require 'cairo'
    ------------------------------------------------------------
    -- CAIRO FALLBACK
    ------------------------------------------------------------
    local ok, cairo_xlib = pcall(require, 'cairo_xlib')
    if not ok then
        cairo_xlib = setmetatable({}, {
            __index = function(_, k) return _G[k] end
        })
    end
    ------------------------------------------------------------
    -- CAL ENGINE WITH MONTHLY CACHE
    ------------------------------------------------------------
    local cal_cache = nil
    local cal_monthname = nil
    local cal_weekdays = nil
    local last_month = nil
    local function update_cal_cache()
        local current_month = os.date("%m")
        if last_month == current_month and cal_cache ~= nil then
            return
        end
        local handle = io.popen("cal")
        local out = handle:read("*a")
        handle:close()
        cal_cache = out
        last_month = current_month
        local lines = {}
        for line in out:gmatch("[^\n]+") do
            table.insert(lines, line)
        end
        -- First line = month name + year
        cal_monthname = lines[1]:gsub("^%s*(.-)%s*$", "%1")
        -- Second line = weekday names
        cal_weekdays = {}
        for wd in lines[2]:gmatch("(%S+)") do
            table.insert(cal_weekdays, wd)
        end
    end
    local function get_cal_monthname()
        update_cal_cache()
        return cal_monthname
    end
    local function get_cal_weekdays()
        update_cal_cache()
        return cal_weekdays
    end
    ------------------------------------------------------------
    -- UTILS
    ------------------------------------------------------------
    function hex_to_rgba(hex, alpha)
        if type(hex) == "string" then
            hex = tonumber(hex:gsub("#", ""), 16)
        end
        local r = ((hex >> 16) & 0xFF) / 255
        local g = ((hex >> 8) & 0xFF) / 255
        local b = (hex & 0xFF) / 255
        return r, g, b, alpha
    end
    ​
    function draw_allowed(flag)
        if flag == nil or flag == true then return true end
        return conky_parse(tostring(flag)) == "1"
    end
    ​
    function get_color_from_list(stops, t)
        local prev = stops[1]
        for i = 2, #stops do
            local nxt = stops[i]
            if t <= nxt[1] then
                local p = (t - prev[1]) / (nxt[1] - prev[1])
                local r1, g1, b1, a1 = hex_to_rgba(prev[2], prev[3])
                local r2, g2, b2, a2 = hex_to_rgba(nxt[2], nxt[3])
                return
                    r1 + (r2 - r1) * p,
                    g1 + (g2 - g1) * p,
                    b1 + (b2 - b1) * p,
                    a1 + (a2 - a1) * p
            end
            prev = nxt
        end
        return hex_to_rgba(prev[2], prev[3])
    end
    ​
    ------------------------------------------------------------
    -- SEPARATOR LINE
    ------------------------------------------------------------
    function draw_separator_line(cr, m)
        local x1, y1 = m.x1, m.y1
        local x2, y2 = m.x2, m.y2
        local thickness = m.thickness or 1
        local fg = m.fg
        cairo_set_line_width(cr, thickness)
        local steps = 200
        local dx = (x2 - x1) / steps
        local dy = (y2 - y1) / steps
        for i = 0, steps - 1 do
            local t = (i + 1) / steps
            local r, g, b, a = get_color_from_list(fg, t)
            cairo_set_source_rgba(cr, r, g, b, a)
            local sx = x1 + dx * i
            local sy = y1 + dy * i
            local ex = x1 + dx * (i + 1)
            local ey = y1 + dy * (i + 1)
            cairo_move_to(cr, sx, sy)
            cairo_line_to(cr, ex, ey)
            cairo_stroke(cr)
        end
    end
    ​
    ------------------------------------------------------------
    -- CONFIG
    ------------------------------------------------------------
    calendar = {
        draw_me        = true,
        month_format   = "year_month",
        x              = 300,
        y              = 15,
        cell_w         = 40,
        row_h          = 30,
        font           = "Noto Sans",
        size           = 18,
        weight         = "bold",
        show_weeknums  = true,
        color_month    = {
            { 1, 0xfcfcfc, 1 },
        },
        color_weekdays = {
            { 1, 0xfcfcfc, 0.7 },
        },
        color_days     = {
            { 1, 0xfcfcfc, 1 },
        },
        color_today    = {
            { 1, 0x3daee9, 1 },
        },
        color_outside  = {
            { 1, 0xfcfcfc, 0.7 },
        },
        color_weeknums = {
            { 1, 0xfcfcfc, 0.5 },
        },
    }
    ------------------------------------------------------------
    -- CALENDAR RENDERER
    ------------------------------------------------------------
    function draw_calendar(cr, cfg)
        if not draw_allowed(cfg.draw_me) then return end
        if conky_window == nil then return end
        local x             = cfg.x
        local y             = cfg.y
        local cw            = cfg.cell_w
        local rh            = cfg.row_h
        local now           = os.date("*t")
        local year          = now.year
        local month         = now.month
        local today         = now.day
        local first         = os.time { year = year, month = month, day = 1 }
        local wday          = tonumber(os.date("%w", first))
        -- cal already gives correct week start
        -- no manual shifting needed
        local days_in_month = os.date("*t", os.time { year = year, month = month + 1, day = 0 }).day
        local prev_days     = os.date("*t", os.time { year = year, month = month, day = 0 }).day
        --------------------------------------------------------
        -- MONTH NAME (from cal)
        --------------------------------------------------------
        local month_raw     = get_cal_monthname()
        local fmt           = cfg.month_format or "month_year"
        local month_name
        if fmt == "month_year" then
            month_name = month_raw
        else
            local ystr = tostring(year)
            local m = month_raw:gsub("%s*" .. ystr .. "%s*", "")
            month_name = ystr .. ". " .. m
        end
        draw_text(cr, {
            text   = month_name,
            x      = x + (cfg.show_weeknums and cw or 0) + cw * 3.5,
            y      = y,
            align  = "center",
            font   = "Roboto",
            size   = cfg.size + 8,
            weight = "bold",
            color  = cfg.color_month,
        })
        --------------------------------------------------------
        -- SEPARATOR
        --------------------------------------------------------
        draw_separator_line(cr, {
            x1 = x,
            y1 = y + rh,
            x2 = x + cw * (cfg.show_weeknums and 8 or 7),
            y2 = y + rh,
            thickness = 1,
            fg = cfg.color_weekdays,
        })
        --------------------------------------------------------
        -- WEEKDAY LABELS (from cal)
        --------------------------------------------------------
        local WEEKDAYS = get_cal_weekdays()
        for i = 1, 7 do
            draw_text(cr, {
                text  = WEEKDAYS[i],
                x     = x + (cfg.show_weeknums and cw or 0) + (i - 1) * cw + cw / 2,
                y     = y + rh * 1.8,
                align = "center",
                font  = cfg.font,
                size  = cfg.size,
                color = cfg.color_weekdays,
            })
        end
        --------------------------------------------------------
        -- DAYS GRID
        --------------------------------------------------------
        local row = 0
        local col = 0
        -- PREVIOUS MONTH DAYS
        for i = wday, 1, -1 do
            draw_text(cr, {
                text  = tostring(prev_days - i + 1),
                x     = x + (cfg.show_weeknums and cw or 0) + col * cw + cw / 2,
                y     = y + rh * (row + 3),
                align = "center",
                font  = cfg.font,
                size  = cfg.size,
                color = cfg.color_outside,
            })
            col = col + 1
        end
        -- CURRENT MONTH DAYS
        for d = 1, days_in_month do
            if col == 7 then
                col = 0
                row = row + 1
            end
            if cfg.show_weeknums and col == 0 then
                local t = os.time { year = year, month = month, day = d }
                draw_text(cr, {
                    text  = os.date("%V", t),
                    x     = x + cw / 2,
                    y     = y + rh * (row + 3),
                    align = "center",
                    font  = cfg.font,
                    size  = cfg.size,
                    color = cfg.color_weeknums,
                })
            end
            local color  = (d == today) and cfg.color_today or cfg.color_days
            local weight = (d == today) and "bold" or "normal"
            draw_text(cr, {
                text   = tostring(d),
                x      = x + (cfg.show_weeknums and cw or 0) + col * cw + cw / 2,
                y      = y + rh * (row + 3),
                align  = "center",
                font   = cfg.font,
                size   = cfg.size,
                color  = color,
                weight = weight,
            })
            col = col + 1
        end
        -- NEXT MONTH DAYS
        local total_cells   = 42
        local current_cells = row * 7 + col
        local next_d        = 1
        while current_cells < total_cells do
            if col == 7 then
                col = 0
                row = row + 1
            end
            draw_text(cr, {
                text  = tostring(next_d),
                x     = x + (cfg.show_weeknums and cw or 0) + col * cw + cw / 2,
                y     = y + rh * (row + 3),
                align = "center",
                font  = cfg.font,
                size  = cfg.size,
                color = cfg.color_outside,
            })
            next_d        = next_d + 1
            col           = col + 1
            current_cells = current_cells + 1
        end
    end
    ​
    ------------------------------------------------------------
    -- CONKY ENTRY POINT
    ------------------------------------------------------------
    function conky_main_calendar()
        if conky_window == nil then return end
        if not calendar then return end
        local cs = cairo_xlib_surface_create(
            conky_window.display,
            conky_window.drawable,
            conky_window.visual,
            conky_window.width,
            conky_window.height
        )
        local cr = cairo_create(cs)
        draw_calendar(cr, calendar)
        cairo_destroy(cr)
        cairo_surface_destroy(cs)
    end
    ​
    
  • background.lua — background and shadow

  • --[[
    ===============================================================================
    Locale‑Aware Conky Clock & Calendar (LACCC)
    BACKGROUND MODULE – Rounded rectangles, gradients, borders using Cairo
    Part of the 2026 ConkySystem Framework
    ===============================================================================
    ​
    OVERVIEW
    --------
    This module draws one or more background panels behind the LACCC components.
    It supports:
    ​
    • rounded rectangle backgrounds,
    • vertical gradient fills,
    • gradient borders drawn inward for pixel‑perfect edges,
    • automatic full‑window sizing when width/height = 0,
    • Cairo fallback when cairo_xlib is unavailable.
    ​
    ===============================================================================
    ]] --
    ​
    require 'cairo'
    ​
    local ok, cairo_xlib = pcall(require, 'cairo_xlib')
    if not ok then
        cairo_xlib = setmetatable({}, {
            __index = function(_, k) return _G[k] end
        })
    end
    ​
    BACKGROUND_DEFAULT = {
        draw_me = true,
        x = 0,
        y = 0,
        w = 0,
        h = 0,
        radius = 20,
    ​
        bg = {
            { 1, 0x141618, 1 },
        },
    ​
        border = {
            { 1.00, 0x4c4e51, 1.00 },
        },
    ​
        border_width = 2,
    }
    ​
    backgrounds = {
    {
        draw_me = true,
        -- draw_me = (has_usb() == 1) or (conky_gpu_mode() ~= "integrated") or (wifi_active() == 1),
        x = 0,
        y = 0,
        w = 0,
        h = 0,
        radius = 8,
    ​
        bg = {
            { 1.00, 0x202326, 0.77 },
        },
    ​
        border = {
            { 1.00, 0x4c4e51, 1.00 },
        },
    ​
        border_width = 2,
    ​
    }
    }
    ​
    local function hex_to_rgba(hex, a)
        local r = ((hex >> 16) & 0xFF) / 255
        local g = ((hex >> 8) & 0xFF) / 255
        local b = (hex & 0xFF) / 255
        return r, g, b, a
    end
    ​
    local function rounded_rect_path(cr, x, y, w, h, r)
        cairo_new_sub_path(cr)
        cairo_arc(cr, x + w - r, y + r, r, -math.pi / 2, 0)
        cairo_arc(cr, x + w - r, y + h - r, r, 0, math.pi / 2)
        cairo_arc(cr, x + r, y + h - r, r, math.pi / 2, math.pi)
        cairo_arc(cr, x + r, y + r, r, math.pi, 3 * math.pi / 2)
        cairo_close_path(cr)
    end
    ​
    local function draw_background(cr, cfg)
        local x = cfg.x
        local y = cfg.y
        local w = (cfg.w == 0) and conky_window.width or cfg.w
        local h = (cfg.h == 0) and conky_window.height or cfg.h
        local r = cfg.radius
        local bw = cfg.border_width
    ​
        --------------------------------------------------------
        -- HÁTTÉR (kerekített, gradient)
        --------------------------------------------------------
        local pat_bg = cairo_pattern_create_linear(x, y, x, y + h)
        for _, s in ipairs(cfg.bg) do
            local pos, col, a = s[1], s[2], s[3]
            local rr, gg, bb, aa = hex_to_rgba(col, a)
            cairo_pattern_add_color_stop_rgba(pat_bg, pos, rr, gg, bb, aa)
        end
    ​
        cairo_set_source(cr, pat_bg)
        rounded_rect_path(cr, x, y, w, h, r)
        cairo_fill(cr)
        cairo_pattern_destroy(pat_bg)
    ​
        --------------------------------------------------------
        -- BORDER (befelé rajzolva, hibamentes)
        --------------------------------------------------------
        local inset = bw / 2
        local inner_x = x + inset
        local inner_y = y + inset
        local inner_w = w - bw
        local inner_h = h - bw
        local inner_r = r - inset
    ​
        if inner_r < 0 then inner_r = 0 end
    ​
        local pat = cairo_pattern_create_linear(x, y, x, y + h)
        for _, stop in ipairs(cfg.border) do
            local pos, col, a = stop[1], stop[2], stop[3]
            local rr, gg, bb, aa = hex_to_rgba(col, a)
            cairo_pattern_add_color_stop_rgba(pat, pos, rr, gg, bb, aa)
        end
    ​
        cairo_set_source(cr, pat)
        cairo_set_line_width(cr, bw)
    ​
        rounded_rect_path(cr, inner_x, inner_y, inner_w, inner_h, inner_r)
        cairo_stroke(cr)
    ​
        cairo_pattern_destroy(pat)
    end
    ​
    function conky_main_background()
        if conky_window == nil then return end
    ​
        local cs = cairo_xlib_surface_create(
            conky_window.display,
            conky_window.drawable,
            conky_window.visual,
            conky_window.width,
            conky_window.height
        )
    ​
        local cr = cairo_create(cs)
    ​
        for _, bg in ipairs(backgrounds) do
            draw_background(cr, bg)
        end
    ​
        cairo_destroy(cr)
        cairo_surface_destroy(cs)
    end
    ​
    
  • text.lua — typography and layout helpers

  • --[[
    ===============================================================================
    Locale‑Aware Conky Clock & Calendar (LACCC)
    TEXT MODULE – Gradient text, alignment, centering, Conky variable expansion
    Part of the 2026 ConkySystem Framework
    ===============================================================================
    ​
    OVERVIEW
    --------
    This module provides a Cairo‑based text renderer for Conky.
    It supports:
    ​
    • gradient text coloring,
    • left/center/right alignment,
    • font slant and weight,
    • automatic Conky variable expansion,
    • optional draw conditions,
    • Wayland‑safe delayed initialization,
    • Cairo fallback when cairo_xlib is unavailable.
    ​
    ===============================================================================
    ]] --
    ​
    require 'cairo'
    ​
    ------------------------------------------------------------
    -- CAIRO FALLBACK
    ------------------------------------------------------------
    local ok, cairo_xlib = pcall(require, 'cairo_xlib')
    if not ok then
        cairo_xlib = setmetatable({}, {
            __index = function(_, k) return _G[k] end
        })
    end
    ​
    ------------------------------------------------------------
    -- GLOBAL UPDATE COUNTER (Wayland stabilization)
    ------------------------------------------------------------
    local update_counter = 0
    local UPDATE_DELAY = 3
    ​
    ------------------------------------------------------------
    -- TEXT BLOCKS
    ------------------------------------------------------------
    ​
    text = {
    ​
    }
    ------------------------------------------------------------
    -- UTILS
    ------------------------------------------------------------
    function hex_to_rgba(hex, alpha)
        if type(hex) == "string" then
            hex = hex:gsub("#", "")
            hex = tonumber(hex, 16)
        end
        local r = ((hex >> 16) & 0xFF) / 255
        local g = ((hex >> 8) & 0xFF) / 255
        local b = (hex & 0xFF) / 255
        return r, g, b, alpha
    end
    ​
    function draw_allowed(flag)
        if flag == nil or flag == true then return true end
        return conky_parse(tostring(flag)) == "1"
    end
    ​
    function normalize_text(opts)
        if not opts or not opts.text then return nil end
        local txt = opts.text
        if opts.conky ~= false then
            txt = conky_parse(txt)
        end
        if not txt or txt == "" then return nil end
        return txt
    end
    ​
    ------------------------------------------------------------
    -- COLOR INTERPOLATION
    ------------------------------------------------------------
    function get_color_from_list(stops, t)
        local prev = stops[1]
        for i = 2, #stops do
            local next = stops[i]
            if t <= next[1] then
                local p = (t - prev[1]) / (next[1] - prev[1])
                local r1, g1, b1, a1 = hex_to_rgba(prev[2], prev[3])
                local r2, g2, b2, a2 = hex_to_rgba(next[2], next[3])
                return
                    r1 + (r2 - r1) * p,
                    g1 + (g2 - g1) * p,
                    b1 + (b2 - b1) * p,
                    a1 + (a2 - a1) * p
            end
            prev = next
        end
        return hex_to_rgba(prev[2], prev[3])
    end
    ​
    ------------------------------------------------------------
    -- DEFAULT TEXT CONFIG
    ------------------------------------------------------------
    TEXT_DEFAULT = {
        text    = "",
        draw_me = true,
        x       = 0,
        y       = 0,
        font    = "Sans",
        size    = 14,
        slant   = "normal",
        weight  = "normal",
        align   = "left",
        color   = {
            { 0, 0xffffff, 1 },
            { 1, 0xffffff, 1 },
        },
    }
    ​
    ------------------------------------------------------------
    -- DRAW TEXT
    ------------------------------------------------------------
    function draw_text(cr, opts)
        if not opts then return end
        if not draw_allowed(opts.draw_me) then return end
    ​
        local cfg = {}
        for k, v in pairs(TEXT_DEFAULT) do cfg[k] = v end
        for k, v in pairs(opts) do cfg[k] = v end
    ​
        local txt = normalize_text(cfg)
        if not txt then return end
    ​
        local slant  = (cfg.slant == "italic") and CAIRO_FONT_SLANT_ITALIC or CAIRO_FONT_SLANT_NORMAL
        local weight = (cfg.weight == "bold") and CAIRO_FONT_WEIGHT_BOLD or CAIRO_FONT_WEIGHT_NORMAL
    ​
        cairo_select_font_face(cr, cfg.font, slant, weight)
        cairo_set_font_size(cr, cfg.size)
    ​
        local ext = cairo_text_extents_t:create()
        cairo_text_extents(cr, txt, ext)
    ​
        local x = cfg.x
        local y = cfg.y
    ​
        if x == "center" then x = conky_window.width / 2 end
        if y == "center" then y = conky_window.height / 2 end
    ​
        if cfg.align == "center" then
            x = x - ext.width / 2
        elseif cfg.align == "right" then
            x = x - ext.width
        end
    ​
        y = y - ext.y_bearing
    ​
        local pat = cairo_pattern_create_linear(x, y, x + ext.width, y)
        for _, stop in ipairs(cfg.color) do
            local pos, hex, alpha = stop[1], stop[2], stop[3]
            local r, g, b, a = hex_to_rgba(hex, alpha)
            cairo_pattern_add_color_stop_rgba(pat, pos, r, g, b, a)
        end
    ​
        cairo_set_source(cr, pat)
        cairo_pattern_destroy(pat)
    ​
        cairo_move_to(cr, x, y)
        cairo_show_text(cr, txt)
    end
    ​
    ------------------------------------------------------------
    -- CONKY ENTRY POINT
    ------------------------------------------------------------
    function conky_main_text()
        update_counter = update_counter + 1
        if update_counter < UPDATE_DELAY then return end
        if conky_window == nil then return end
        if not text then return end
    ​
        local cs = cairo_xlib_surface_create(
            conky_window.display,
            conky_window.drawable,
            conky_window.visual,
            conky_window.width,
            conky_window.height
        )
    ​
        local cr = cairo_create(cs)
    ​
        for _, t in ipairs(text) do
            draw_text(cr, t)
        end
    ​
        cairo_destroy(cr)
        cairo_surface_destroy(cs)
    end
    ​
    

:framed_picture: Screenshots

:inbox_tray: Installation

  1. Copy the folder to:

Code

~/.conky/calendar_clock/
  1. Make the autostart script executable:

Code

chmod +x ~/.conky/calendar_clock/autostart.sh
  1. Add it to your session autostart:

Code

~/.config/autostart-scripts/
  1. Log out and back in.

Conky config

--[[
===============================================================================
Locale‑Aware Conky Clock & Calendar (LACCC)
LACC.CONF – Core Conky configuration for the LACCC framework
Part of the 2026 ConkySystem Framework
===============================================================================
​
OVERVIEW
--------
This file defines the Conky window, rendering behavior, transparency settings,
and the Lua module loader for the LACCC system. It provides the environment
in which all LACCC components (clock, calendar, background, text) are drawn.
​
The configuration is optimized for all major desktop environments and ensures
stable ARGB rendering, correct window stacking, and consistent behavior across
sessions.
​
===============================================================================
]]
​
​
conky.config = {
use_xft = true,
font = 'DejaVu Sans:size=10',
xftalpha = 1,
background = true,
double_buffer = true,
no_buffers = true,
text_buffer_size = 2048,
​
update_interval = 1,
cpu_avg_samples = 2,
net_avg_samples = 2,
​
alignment = 'top_right',
gap_x = 10,
gap_y = 10,
​
minimum_width = 630,
maximum_width = 630,
minimum_height = 290,
​
own_window = true,
own_window_type = 'normal',
own_window_title = 'Conky',
own_window_hints = 'undecorated,below,sticky,skip_taskbar,skip_pager',
​
own_window_transparent = true,
own_window_argb_visual = true,
​
lua_load = './main.lua',
lua_draw_hook_post = 'main',
​
border_inner_margin = 0,
border_outer_margin = 0,
border_width = 0,
​
default_color = '#ededed',
pad_percents = 0,
extra_newline = false,
​
};
​
conky.text = [[
​
]]
​

Notes

If someone experiences startup issues, it’s almost never a Conky bug. Typical causes are:

  • wrong working directory

  • incorrect paths

  • Conky starting too early in the session

With the structure and autostart script above, the setup is stable on all major desktop environments.

4 Likes

Works well with Plasma 6, but one KDE shortcut needs to be adjusted:

On Plasma 6 the default Win + D action is “Show Desktop”, not “Minimize All Windows”.
However, Plasma’s “Show Desktop” hides all windows, including Conky, so Conky appears to disappear.

To keep Conky visible, assign Win + D to “Minimize All Windows” instead:

System Settings → Shortcuts → Window Management → Minimize All Windows → assign Win + D

After this, Conky will stay on screen when you press Win + D.

1 Like

Wow - I used to have this shortcut on a mouse gesture with Plasma 5.

I cannot find ‘Minimize All Windows’ now!

First search kwin and enable it plasma5 Idont know but plasma 6 like works

1 Like

Ah, ok thx - I’d disabled the kwin script so didn’t have a shortcut.

Would there be much involved in setting this explicitly at my end, or to add it as an option?
… e.g. for UK date format but Sunday as start of week. :smiley:

Sorry, if it were 6502 code I could probably do it myself without having to ask. :winking_face_with_tongue:

“The beginning of the week and the weekday order come directly from the system’s cal command. I designed it this way intentionally, because cal already follows the user’s locale. So if someone uses the computer in Hungarian, they automatically get Monday as the first day of the week. If someone uses a locale where Sunday is the first day, they get that instead. No manual configuration is needed, it always matches the user’s language and region settings.”

1 Like

Thanks, looking at the man page for cal now. So it seems it will just follow whatever modifications I have made there. :thinking:

I don’t really understand the issue. Using cal is simply the most logical and standards‑compliant way to generate a multilingual calendar. It automatically follows the user’s locale, which is exactly what a system‑integrated calendar should do.

1 Like

I didn’t suggest it was an “issue” :wink: just that if I can make those settings, your program will have the desired result as you described. :+1:

Your contribution is greatly appreciated! This was never meant as a criticism. It’s just that I found certain programs don’t seem to respect such settings. :wink:

In line 45
local handle = io.popen("cal")
replace it with
local handle = io.popen("cal -s")

man cal

1 Like