diff --git a/README.md b/README.md index 4e59c6d..254b5c2 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,18 @@ # lxpanel-dtime -Patch for lxpanel that allows displaying erisian time \ No newline at end of file +Patch for lxpanel that allows displaying erisian time. Preparing **lxpanel 0.11.1** for build and installation, featuring an enhanced Digital Clock: display of Erisian (Discordian) time via formatting codes. + +Build scripts for Debian 13 and Gentoo. Written by Qwen 3.8 27B. + +| Code | Value | +|-------|-------------------------------------------------------------| +| `%eY` | discordian yaer (YOLD) | +| `%ey` | erisian year (YOLD−3000) | +| `%eS` | name of season (Chaos…Aftermath) | +| `%eE` | season number (1–5) | +| `%ed` | day of the season (1–73) | +| `%eh` | erisian hour (0–9) | +| `%em` | erisian minute (00–99) | +| `%es` | erisian second (00–99) | +| `%ew` | discordian name of the day of the week | +| `%ut` | unix time | \ No newline at end of file diff --git a/README_gentoo.md b/README_gentoo.md new file mode 100644 index 0000000..1855de1 --- /dev/null +++ b/README_gentoo.md @@ -0,0 +1,73 @@ +# lxpanel с эридианскими часами (Digital Clock) + +Готовит к сборке и установке **lxpanel 0.11.1** с расширенным Digital Clock: +эридианское (дискордианское) время + unix time через коды форматирования, плюс +оптимизация вычислений (кэш даты дня, вывод дек. времени вычитанием). + +## Состав + +| Файл | Назначение | +|----------------------|--------------------------------------------------------| +| `build.sh` | скрипт: проверка deps → скачивание исходника → патч → сборка → установка | +| `erisian-time.patch` | патч: `plugins/dclock.c` + `po/lxpanel.pot` | + +## Быстрый старт (Gentoo) + +```sh +tar xf lxpanel-gentoo.tar.gz # если передаёте каталог архивом +cd lxpanel-gentoo + +./build.sh --deps # установит ebuild'ы зависимостей и соберёт +# далее, если нужно: +./build.sh --prefix=/opt/lxpanel # в другую директорию +./build.sh --no-install # только собрать +``` + +Всё (кроме `--deps`) работает и без root; установка идёт в `$PREFIX` (по умолчанию `/usr`). + +## Зависимости (Gentoo ebuild) + +Обязательные (проверяются `pkg-config`): +`gtk+-3.0`, `libfm-gtk3`, `libwnck-3.0`, `keybinder-3.0`, `glib-2.0`, `x11`. + +Опциональные (для динамических плагинов по умолчанию): +`libmenu-cache` (меню), `net-misc/curl` + `dev-libs/libxml2` (weather), +`net-wireless/libiw` (netstat), `media-libs/alsa-lib` (volume). +Если плагин без своей зависимости — скрипт **сам отключит** этот плагин, сборка пройдёт. + +Инструменты сборки: `sys-devel/autoconf`, `sys-devel/automake`, `sys-devel/libtool`, +`sys-apps/intltool`. + +> Альтернатива: `emerge lxpanel` поставит все зависимости сразу (stock-версию), +> а `./build.sh` поверх соберёт нашу. + +## Что меняется в Digital Clock (коды форматирования) + +В поле *Clock Format* / *Tooltip Format* (помимо стандартных `strftime`): + +| Код | Значение | +|-------|-------------------------------------------------------------| +| `%eY` | эридианский год (YOLD) | +| `%ey` | краткий год (YOLD − 3000; григорианский год − 1834) | +| `%eS` | сезон — название (Chaos…Aftermath) | +| `%eE` | сезон — порядковый № (1–5) | +| `%ed` | день сезона (1–73); в день Тиба — `tib` | +| `%eh` | дек. час (0–9) | +| `%em` | дек. минута (00–99) | +| `%es` | дек. секунда (00–99) | +| `%ew` | день недели (один из пяти); в день Тиба — `St. Tib's Day` | +| `%ut` | unix time | + +`%%` — литеральный `%`. Пример формата: +`%eY.%eE.%ed %eh:%em %ew (%Y.%m.%d %R)` + +## Примечания + +* Скрипт качает исходник с **закреплённого коммита** `4dec3d0d` (тот, против + которого сделан патч) — `https://codeload.github.com/lxde/lxpanel/tar.gz/4dec3d0d…`. + Не подставляйте тег `0.11.1` — это 3 коммита раньше, патч может не лечь. +* `make install` кладёт бинарь/плагины/иконки в `$PREFIX`. Если lxpanel уже + стоит через `emerge`, наш `make install` перезапишет файлы; для «чистоты» + портрeе можно `emerge -C lxpanel` или собрать в отдельный `--prefix`. +* Эридианские часы — **встроенный** плагин (`plugins/dclock.c`), собирается в + сам `lxpanel` (не отдельный `.so`), поэтому для него не нужны опции `--with-plugins`. diff --git a/build_deb.sh b/build_deb.sh new file mode 100755 index 0000000..aea985c --- /dev/null +++ b/build_deb.sh @@ -0,0 +1,70 @@ +#!/usr/bin/env bash +# +# build_deb.sh — build the lxpanel .deb with the Erisian/Unix-time +# Digital Clock. +# +# Source: the official Debian source package (3.0 quilt) is unpacked with +# the Debian tool "dpkg-source -x", and our modification is applied on top: +# +# erisian-time.patch (Digital Clock: christian / erisian / unix time) +# +# One-time setup (needs root): +# sudo apt-get update +# sudo apt-get install -y dpkg-dev apt-utils build-essential autoconf automake libtool +# sudo apt-get build-dep lxpanel +# +# Build (no root needed): +# ./build_deb.sh +# +set -euo pipefail + +# Use system build tools only — a foreign toolchain earlier in PATH (e.g. +# ~/linuxbrew) breaks the final link (libX11 -> libxcb undefined refs). +export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + +HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$HERE" + +PKG=lxpanel +VER=0.11.1 +DEBREV=1 +SRC_DIR="$PKG-$VER" +DSC="${PKG}_${VER}-${DEBREV}.dsc" +ORIG_TAR="${PKG}_${VER}.orig.tar.gz" +DEB_TAR="${PKG}_${VER}-${DEBREV}.debian.tar.xz" +PATCH=erisian-time.patch +POOL="http://deb.debian.org/debian/pool/main/l/lxpanel" + +# --- 0. sanity ------------------------------------------------------------- +for t in dpkg-source curl patch; do + command -v "$t" >/dev/null 2>&1 || { echo "ERROR: '$t' not found (install dpkg-dev/curl/patch)."; exit 1; } +done + +# --- 1. make sure the Debian source tree is unpacked (quilt patches applied) -- +if [ ! -d "$SRC_DIR/debian" ]; then + echo ">> Unpacking Debian source ($DSC) ..." + [ -f "$DSC" ] || curl -fsS -o "$DSC" "$POOL/$DSC" + [ -f "$ORIG_TAR" ] || curl -fsS -o "$ORIG_TAR" "$POOL/$ORIG_TAR" + [ -f "$DEB_TAR" ] || curl -fsS -o "$DEB_TAR" "$POOL/$DEB_TAR" + dpkg-source -x "$DSC" +fi + +# --- 2. apply our modification (idempotent) -------------------------------- +if grep -q "dclock_expand_codes" "$SRC_DIR/plugins/dclock.c"; then + echo ">> $PATCH already applied — skipping." +else + echo ">> Applying $PATCH ..." + ( cd "$SRC_DIR" && patch -p1 < "$HERE/$PATCH" ) +fi + +# --- 3. build the binary .deb -------------------------------------------- +echo ">> Building .deb with dpkg-buildpackage ..." +( cd "$SRC_DIR" && dpkg-buildpackage -b -us -uc ) + +# --- 4. report ----------------------------------------------------------- +echo +echo ">> Built packages (in $HERE):" +ls -1 "$HERE"/*.deb +echo +echo "Install with:" +echo " sudo dpkg -i $(ls -1 "$HERE"/${PKG}_*.deb | tr '\n' ' ')" diff --git a/build_gentoo.sh b/build_gentoo.sh new file mode 100755 index 0000000..01a56dc --- /dev/null +++ b/build_gentoo.sh @@ -0,0 +1,143 @@ +#!/usr/bin/env bash +# +# build.sh — build & install lxpanel with the Erisian/Unix-time Digital Clock. +# +# Target: Gentoo Linux (out-of-tree source build). The same script works on any +# autotools-based distro; only the emerge convenience list is Gentoo-specific. +# +# The build: +# 1. verifies build dependencies (pkg-config; optionally `emerge`s them), +# 2. downloads lxpanel at the EXACT commit the patch was generated against, +# 3. applies erisian-time.patch (idempotent), +# 4. runs autogen.sh + configure + make, and (optionally) `make install`. +# +# Usage: +# ./build.sh # build & install to /usr (default) +# ./build.sh --deps # also `emerge` the required ebuilds first +# ./build.sh --prefix=/opt/lxpanel +# ./build.sh --no-install # build only, do not install +# ./build.sh --clean # re-download the source fresh +# +set -euo pipefail + +HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$HERE" + +# Prefer system build tools: a foreign toolchain earlier in PATH (e.g. a +# linuxbrew gcc/ld) can break the final link. On a clean Gentoo this is a no-op. +export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + +# --- pinned upstream commit (erisian-time.patch was generated against this) -- +COMMIT=4dec3d0d3d8f9e2a9d14dd1b099d378289b67db4 +SRC_URL="https://codeload.github.com/lxde/lxpanel/tar.gz/${COMMIT}" +SRC_TAR="lxpanel-src.tar.gz" +SRC_DIR="lxpanel-${COMMIT}" +PATCH=erisian-time.patch + +PREFIX=/usr +DO_DEPS=0 +DO_INSTALL=1 +DO_CLEAN=0 + +for arg in "$@"; do + case "$arg" in + --deps) DO_DEPS=1 ;; + --no-install) DO_INSTALL=0 ;; + --clean) DO_CLEAN=1 ;; + --prefix=*) PREFIX="${arg#--prefix=}" ;; + *) echo "Unknown option: $arg (see header)"; exit 1 ;; + esac +done + +# run priv-privileged commands with sudo only when not root +if [ "$(id -u)" = 0 ]; then SUDO=""; else SUDO="sudo"; fi + +# --- 1. dependencies -------------------------------------------------------- +# pkg-config modules that configure.ac requires for the GTK3 (default) build. +REQUIRED_PC=(gtk+-3.0 libfm-gtk3 libwnck-3.0 keybinder-3.0 glib-2.0 x11) +OPTIONAL_PC=(libmenu-cache) # without: menu routines are disabled (warning) + +# Gentoo ebuilds providing the above + the default dynamic-plugin deps. +# (best-effort; the pkg-config checks below are the authoritative gate) +EBUILDS=( + sys-devel/autoconf sys-devel/automake sys-devel/libtool sys-apps/intltool + dev-libs/glib x11-libs/gtk+ gnome-extra/libfm-gtk gnome-libs/libwnck + x11-libs/keybinder gnome-extra/libmenu-cache x11-libs/libX11 + net-misc/curl dev-libs/libxml2 net-wireless/libiw media-libs/alsa-lib +) + +if [ "$DO_DEPS" = 1 ]; then + echo ">> Installing dependencies via emerge ..." + $SUDO emerge -av "${EBUILDS[@]}" +fi + +echo ">> Checking dependencies (pkg-config) ..." +missing=0 +for m in "${REQUIRED_PC[@]}"; do + if pkg-config --exists "$m" 2>/dev/null; then echo " [ok] $m" + else echo " [MISS] $m"; missing=1; fi +done +for m in "${OPTIONAL_PC[@]}"; do + if pkg-config --exists "$m" 2>/dev/null; then echo " [ok] $m" + else echo " [opt] $m (menu support will be disabled)"; fi +done +if [ "$missing" = 1 ]; then + echo; echo "Missing required dependencies — install the ebuilds above (or rerun with --deps)." + exit 1 +fi + +# --- 2. source (pinned commit) --------------------------------------------- +if [ "$DO_CLEAN" = 1 ]; then rm -rf "$SRC_DIR" "$SRC_TAR"; fi +if [ ! -d "$SRC_DIR" ]; then + echo ">> Downloading lxpanel @ ${COMMIT:0:7} ..." + curl -fsSL -o "$SRC_TAR" "$SRC_URL" + tar xf "$SRC_TAR" +fi +cd "$SRC_DIR" + +# --- 3. apply the Erisian patch (idempotent) -------------------------------- +if grep -q "dclock_expand_codes" plugins/dclock.c; then + echo ">> ${PATCH} already applied — skipping." +else + echo ">> Applying ${PATCH} ..." + patch -p1 < "$HERE/$PATCH" +fi + +# --- 4. build -------------------------------------------------------------- +# Auto-disable dynamic plugins whose optional deps are absent, so the build +# succeeds on a minimal system. The Erisian clock (plugins/dclock.c) is a +# built-in plugin compiled into lxpanel itself and is ALWAYS built. +EXCLUDES="" +if ! command -v curl-config >/dev/null 2>&1 || ! pkg-config --exists libxml-2.0 2>/dev/null; then + EXCLUDES="${EXCLUDES},-weather"; echo " [note] weather plugin disabled (need: net-misc/curl, dev-libs/libxml2)" +fi +if [ ! -e /usr/include/iwlib.h ] && [ ! -e /usr/include/linux/iwlib.h ]; then + EXCLUDES="${EXCLUDES},-netstat"; echo " [note] netstat plugin disabled (need: net-wireless/libiw)" +fi +WITH_PLUGINS_OPT="" +[ -n "$EXCLUDES" ] && WITH_PLUGINS_OPT="--with-plugins=all${EXCLUDES}" + +echo ">> autogen.sh ..." +sh ./autogen.sh +# GTK3 is NOT the configure default (upstream defaults to gtk+-2.0), so +# --enable-gtk3=yes is required for the modern gtk+-3.0 build. +echo ">> configure --enable-gtk3=yes --prefix=$PREFIX $WITH_PLUGINS_OPT" +./configure --enable-gtk3=yes --prefix="$PREFIX" $WITH_PLUGINS_OPT +echo ">> make ..." +make +if [ "$DO_INSTALL" = 1 ]; then + echo ">> make install ..." + $SUDO make install +fi + +echo +if [ "$DO_INSTALL" = 1 ]; then + echo ">> Done. lxpanel installed under $PREFIX." +else + echo ">> Done. (built only — not installed)" +fi +echo " Configure the Digital Clock (Clock Format) with the Erisian/unix codes, e.g.:" +echo " %eY.%eE.%ed %eh:%em:%es %ew" +echo " Codes: %eY year / %ey short-year / %eS season / %eE season# / %ed day" +echo " %eh:%em:%es hour:min:sec / %ew weekday / %ut unix time" +echo " St Tib's day (leap year): %ed=tib, %ew=St. Tib's Day" diff --git a/erisian-time.patch b/erisian-time.patch new file mode 100644 index 0000000..1d28c56 --- /dev/null +++ b/erisian-time.patch @@ -0,0 +1,264 @@ +diff --git a/plugins/dclock.c b/plugins/dclock.c +index 1a7fe187..2b87a287 100644 +--- a/plugins/dclock.c ++++ b/plugins/dclock.c +@@ -169,6 +169,202 @@ static void dclock_timer_set(DClockPlugin * dc, struct timeval *current_time) + dc->timer = g_timeout_add(milliseconds, (GSourceFunc) dclock_update_display, (gpointer) dc); + } + ++/* Erisian (Discordian) calendar support, ported from dtime-js ++ * (https://gitlab.com/zlax/dtime-js). ++ * A year has five 73-day seasons plus St. Tib's Day in leap years. ++ * A day has ten decimal hours, each a hundred decimal minutes of a hundred ++ * decimal seconds (one decimal second equals 864 christian milliseconds). ++ */ ++typedef struct { ++ int yold; /* Erisian year (YOLD) */ ++ int season; /* 0-4 */ ++ int day; /* 1-73 within the season */ ++ int hour; /* 0-9 decimal hour */ ++ int minute; /* 0-99 decimal minute */ ++ int second; /* 0-99 decimal second */ ++ int st_tibs; /* 1 on St. Tib's Day */ ++ char season_name[16]; /* e.g. "Bureaucracy" */ ++ char weekday_name[32]; /* one of five */ ++} DClockErisTime; ++ ++/* Easter Island winter time offset used by dtime-js, in seconds. */ ++#define DCLOCK_ERIS_TZ_OFFSET (-5 * 60 * 60) ++ ++static const char *dclock_eris_seasons[5] = { ++ "Chaos", "Discord", "Confusion", "Bureaucracy", "Aftermath" ++}; ++ ++static const char *dclock_eris_weekdays[5] = { ++ "Sweetmorn", "Boomtime", "Pungenday", "Prickle-Prickle", "Setting Orange" ++}; ++ ++/* Cumulative days before each month (January = index 0); index 12 = total ++ * days in the year. Turns a Gregorian date into a day-of-year by lookup. */ ++static const unsigned short dclock_eris_cum_common[13] = { ++ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 ++}; ++static const unsigned short dclock_eris_cum_leap[13] = { ++ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 ++}; ++ ++static int dclock_eris_leap(int year) ++{ ++ return (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)); ++} ++ ++/* Day-of-year (1-based) from a Gregorian date, via the cumulative-day table. */ ++static int dclock_eris_day_of_year(int day, int month, int year) ++{ ++ const unsigned short * cum = dclock_eris_leap(year) ++ ? dclock_eris_cum_leap ++ : dclock_eris_cum_common; ++ return (int)(cum[month - 1] + day); ++} ++ ++/* Cached Erisian "day anchor". The Erisian date depends only on the (offset) ++ * UTC calendar day, so it changes at most once per day. This structure is ++ * refreshed only when that day rolls over; between refreshes the decimal time ++ * is derived cheaply (a subtraction from day_start, no modulo). */ ++typedef struct { ++ int64_t day_start; /* shifted unix sec of 00:00:00 of the current day */ ++ int64_t next; /* shifted unix sec at which the date changes */ ++ int yold, season, day, st_tibs; ++ char weekday_name[32]; ++ int valid; ++} DClockErisDay; ++ ++static DClockErisDay eris_day = { 0 }; ++ ++static void dclock_eris_now(const struct timeval *now, DClockErisTime *e); ++static char *dclock_expand_codes(const char *fmt, const DClockErisTime *eris, ++ long long unix_sec); ++ ++/* Compute the Erisian date (cached; refreshed only when the shifted UTC day ++ * changes) and the decimal time (every call) for the current moment. */ ++static void dclock_eris_now(const struct timeval *now, DClockErisTime *e) ++{ ++ time_t shifted_sec = (time_t)now->tv_sec + DCLOCK_ERIS_TZ_OFFSET; ++ ++ /* Refresh the cached Erisian date only when the shifted UTC day changes — ++ * at most once per day. The recompute branch is rare, so mark it unlikely ++ * to keep the common cache-hit path lean. */ ++ if (!eris_day.valid || __builtin_expect(shifted_sec >= eris_day.next, 0)) { ++ struct tm utc; ++ int dom, mon, year, doy, st_tibs; ++ int season, day, yold, sec_since_midnight; ++ ++ gmtime_r(&shifted_sec, &utc); ++ dom = utc.tm_mday; ++ mon = utc.tm_mon + 1; ++ year = utc.tm_year + 1900; ++ ++ doy = dclock_eris_day_of_year(dom, mon, year); ++ st_tibs = dclock_eris_leap(year) && (doy == 60); ++ if (dclock_eris_leap(year) && doy > 60) ++ doy--; ++ season = doy / 73; ++ day = doy % 73; ++ if (day == 0) { ++ season--; ++ day = 73; ++ } ++ yold = year + 1166; ++ ++ /* Anchor of the current shifted UTC day (00:00:00) and its end. */ ++ sec_since_midnight = utc.tm_hour * 3600 + utc.tm_min * 60 + utc.tm_sec; ++ eris_day.day_start = (int64_t)shifted_sec - sec_since_midnight; ++ eris_day.next = eris_day.day_start + 86400; ++ ++ eris_day.yold = yold; ++ eris_day.season = season; ++ eris_day.day = day; ++ eris_day.st_tibs = st_tibs; ++ if (st_tibs) ++ eris_day.weekday_name[0] = '\0'; ++ else ++ snprintf(eris_day.weekday_name, sizeof(eris_day.weekday_name), ++ "%s", dclock_eris_weekdays[(doy - 1) % 5]); ++ eris_day.valid = 1; ++ } ++ ++ e->yold = eris_day.yold; ++ e->season = eris_day.season; ++ e->day = eris_day.day; ++ e->st_tibs = eris_day.st_tibs; ++ snprintf(e->season_name, sizeof(e->season_name), "%s", ++ dclock_eris_seasons[eris_day.season]); ++ memcpy(e->weekday_name, eris_day.weekday_name, sizeof(e->weekday_name)); ++ ++ /* Decimal time: seconds (and ms) into the current day, derived from the ++ * cached day_start by subtraction (no modulo). ds = decimal-second 0-99999. */ ++ int64_t secs_into_day = (int64_t)shifted_sec - eris_day.day_start; ++ int64_t ms_into_day = secs_into_day * 1000 + (int64_t)(now->tv_usec / 1000); ++ int64_t ds = ms_into_day / 864; ++ e->hour = (int)(ds / 10000); ++ e->minute = (int)((ds % 10000) / 100); ++ e->second = (int)(ds % 100); ++} ++ ++/* Expand the non-strftime codes (%e* and %ut) in a format string, leaving the ++ * remaining % codes for strftime(). Returns a newly allocated string. */ ++static char *dclock_expand_codes(const char *fmt, const DClockErisTime *eris, ++ long long unix_sec) ++{ ++ GString *s = g_string_new(NULL); ++ const char *p; ++ ++ if (fmt == NULL) ++ return g_string_free(s, FALSE); ++ ++ for (p = fmt; *p; ) { ++ int advanced = 0; ++ if (*p == '%') { ++ if (p[1] == '%') { ++ g_string_append(s, "%%"); /* literal % — leave for strftime() */ ++ advanced = 2; ++ } ++ else if (p[1] == 'e') { ++ switch (p[2]) { ++ case 'Y': g_string_append_printf(s, "%d", eris->yold); advanced = 3; break; ++ case 'y': g_string_append_printf(s, "%d", eris->yold - 3000); advanced = 3; break; ++ case 'S': g_string_append(s, eris->season_name); advanced = 3; break; ++ case 'E': g_string_append_printf(s, "%d", eris->season + 1); advanced = 3; break; ++ case 'd': ++ if (eris->st_tibs) ++ g_string_append(s, "tib"); ++ else ++ g_string_append_printf(s, "%d", eris->day); ++ advanced = 3; ++ break; ++ case 'h': g_string_append_printf(s, "%d", eris->hour); advanced = 3; break; ++ case 'm': g_string_append_printf(s, "%02d", eris->minute); advanced = 3; break; ++ case 's': g_string_append_printf(s, "%02d", eris->second); advanced = 3; break; ++ case 'w': ++ if (eris->st_tibs) ++ g_string_append(s, "St. Tib's Day"); ++ else ++ g_string_append(s, eris->weekday_name); ++ advanced = 3; ++ break; ++ default: break; ++ } ++ } ++ else if (p[1] == 'u' && p[2] == 't') { ++ g_string_append_printf(s, "%lld", unix_sec); ++ advanced = 3; ++ } ++ } ++ if (advanced) { ++ p += advanced; ++ continue; ++ } ++ g_string_append_c(s, *p); ++ p++; ++ } ++ ++ return g_string_free(s, FALSE); ++} ++ + /* Periodic timer callback. + * Also used during initialization and configuration change to do a redraw. */ + static gboolean dclock_update_display(DClockPlugin * dc) +@@ -183,15 +379,24 @@ static gboolean dclock_update_display(DClockPlugin * dc) + dclock_timer_set(dc, &now); + current_time = localtime(&now.tv_sec); + +- /* Determine the content of the clock label and tooltip. */ +- char clock_value[64]; +- char tooltip_value[64]; ++ /* Determine the content of the clock label and tooltip. ++ * Compute the Erisian date/time once (cached date, cheap decimal time), then ++ * expand the Erisian/unix codes in both formats before strftime. */ ++ DClockErisTime eris; ++ dclock_eris_now(&now, &eris); ++ char * expanded_clock = dclock_expand_codes(dc->clock_format, &eris, (long long)now.tv_sec); ++ char * expanded_tooltip = dclock_expand_codes(dc->tooltip_format, &eris, (long long)now.tv_sec); ++ ++ char clock_value[128]; ++ char tooltip_value[128]; + clock_value[0] = '\0'; + if (dc->clock_format != NULL) +- strftime(clock_value, sizeof(clock_value), dc->clock_format, current_time); ++ strftime(clock_value, sizeof(clock_value), expanded_clock, current_time); + tooltip_value[0] = '\0'; + if (dc->tooltip_format != NULL) +- strftime(tooltip_value, sizeof(tooltip_value), dc->tooltip_format, current_time); ++ strftime(tooltip_value, sizeof(tooltip_value), expanded_tooltip, current_time); ++ g_free(expanded_clock); ++ g_free(expanded_tooltip); + + /* When we write the clock value, it causes the panel to do a full relayout. + * Since this function may be called too often while the timing experiment is underway, +@@ -435,6 +640,9 @@ static GtkWidget *dclock_configure(LXPanel *panel, GtkWidget *p) + _("Clock Format"), &dc->clock_format, CONF_TYPE_STR, + _("Tooltip Format"), &dc->tooltip_format, CONF_TYPE_STR, + _("Format codes: man 3 strftime; %n for line break"), NULL, CONF_TYPE_TRIM, ++ _("Erisian/unix: %eY year %ey short-year %eS season %eE season# %ed day " ++ "%eh:%em:%es hour:min:sec %ew weekday (St Tib's day: %ed=tib, %ew=St. Tib's Day) " ++ "%ut unix time"), NULL, CONF_TYPE_TRIM, + _("Action when clicked (default: display calendar)"), &dc->action, CONF_TYPE_STR, + _("Bold font"), &dc->bold, CONF_TYPE_BOOL, + _("Tooltip only"), &dc->icon_only, CONF_TYPE_BOOL, +diff --git a/po/lxpanel.pot b/po/lxpanel.pot +index dc6b1b07..e6e86067 100644 +--- a/po/lxpanel.pot ++++ b/po/lxpanel.pot +@@ -716,6 +716,12 @@ msgstr "" + msgid "Format codes: man 3 strftime; %n for line break" + msgstr "" + ++#: ../plugins/dclock.c ++msgid "Erisian/unix: %eY year %ey short-year %eS season %eE season# %ed day " ++"%eh:%em:%es hour:min:sec %ew weekday (St Tib's day: %ed=tib, %ew=St. Tib's Day) " ++"%ut unix time" ++msgstr "" ++ + #: ../plugins/dclock.c:438 + msgid "Action when clicked (default: display calendar)" + msgstr ""