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
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.
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
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
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
Screenshots
Installation
- Copy the folder to:
Code
~/.conky/calendar_clock/
- Make the autostart script executable:
Code
chmod +x ~/.conky/calendar_clock/autostart.sh
- Add it to your session autostart:
Code
~/.config/autostart-scripts/
- 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.



