aboutsummaryrefslogtreecommitdiffstats
path: root/bin/calendar-sample.sh
blob: af583f645a17b8633a5bbd493cee0f5fc17f342b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#!/bin/bash
# Sample khal's upcoming events into a cache file, for widgets/calendar.lua.
#
# Conky's Lua has no way to run a process, and shelling out to khal every two
# seconds would fork a Python interpreter per draw. khal takes ~200ms to start,
# which is two orders of magnitude more than the whole frame budget.

set -u

CACHE_DIR="${XDG_CACHE_HOME:-$HOME/.cache}/udt"
CACHE="$CACHE_DIR/calendar.txt"

umask 077
mkdir -p "$CACHE_DIR"

# Without khal the list below fails silently (its stderr is discarded), and the
# cache would claim an empty week. Removing the cache instead makes the card
# show its "no calendar data" face, which is the truth.
if ! command -v khal >/dev/null 2>&1; then
    rm -f "$CACHE"
    exit 1
fi

TMP="$CACHE.tmp.$$"
trap 'rm -f "$TMP"' EXIT

# {start-date-long}, not {start-date}: the latter renders with khal's
# `dateformat`, which here is '%d.%m.' with no year at all, so an event in
# January sorts before one in December of the year before and the widget cannot
# tell which is which. The long form uses `longdateformat` and carries the
# year. Both are the user's own format strings and khal offers no ISO override
# (a strftime spec inside the field, '{start-date:%Y-%m-%d}', raises), so the
# widget parses the three numbers out by position rather than assuming an
# order.
#
# --day-format '' suppresses the 'Today, 18.09.2026' header lines khal
# interleaves between days; without it every group of events is preceded by a
# line that does not parse as a record.
#
# Fields are pipe-separated: a title may contain anything except a newline, so
# the separator has to be a character no date, time or calendar name uses, and
# the parser splits on the first four only, leaving the title intact even if it
# contains a pipe itself.
#
# 8d, not 7d: khal's range is exclusive of the final day in practice for
# all-day events, and one extra day costs nothing but avoids a birthday
# vanishing from the card on the morning it matters.
EVENTS=$(khal list --day-format '' \
    --format '{start-date-long}|{start-time}|{end-time}|{calendar}|{title}' \
    --notstarted today 8d 2>/dev/null)

# khal's exit status is 0 even with no calendars configured, so emptiness is
# not distinguishable from failure by status alone. The widget treats a missing
# 'generated' key as "the sampler never ran" and an empty event list as "no
# events", which are genuinely different states and read differently on the
# card.
{
    echo "generated $(date +%s)"
    date +'today %Y-%m-%d'
    # Calendar colours, as khal itself declares them. Parsed from khal's config
    # rather than hardcoded, so adding a calendar does not mean editing Lua.
    # Only the [[name]] / color = value pairs inside [calendars]; the section
    # test stops at the next top-level [section] so a 'color' key elsewhere in
    # the config cannot be misread as a calendar's.
    awk '
        /^\[calendars\]/ { in_cal = 1; next }
        /^\[[^[]/        { in_cal = 0 }
        in_cal && /^\[\[/ {
            name = $0
            gsub(/^\[\[|\]\]$/, "", name)
            next
        }
        in_cal && /^[ \t]*color[ \t]*=/ {
            sub(/^[^=]*=[ \t]*/, "")
            if (name != "") print "color " name " " $0
        }
    ' "${XDG_CONFIG_HOME:-$HOME/.config}/khal/config" 2>/dev/null

    if [ -n "$EVENTS" ]; then
        printf '%s\n' "$EVENTS" | sed 's/^/event /'
    fi
} > "$TMP"

mv -f "$TMP" "$CACHE"