This commit is contained in:
zlax committed 2026-11-05 22:14:12 +03:00
1 parent 968a4b5842
commit 9f51dc1687
6 files changed
+567 -3

No files matched your search

+2 -2
View File
@@ -12,11 +12,11 @@ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. Do what thou wilt shall be the whole of the Law. 0. Do what thou wilt shall be the whole of the Law.
DWTWL – a license with a single requirement: DO WHAT THOU WILT DWTWL - a license with a single requirement: DO WHAT THOU WILT
The license provides more freedom than any other one (such as GPL or BSD) and does not require saving the license text on copying. The license provides more freedom than any other one (such as GPL or BSD) and does not require saving the license text on copying.
DWTWL – an accomplished and eligible license for free text, code and any other symbols (including the software, documentation and artwork). DWTWL - an accomplished and eligible license for free text, code and any other symbols (including the software, documentation and artwork).
The license does not contain a "no warranty" clause. DWTWL can be used in countries that do not legally acknowledge the transition to public domain. The license does not contain a "no warranty" clause. DWTWL can be used in countries that do not legally acknowledge the transition to public domain.
+16 -1
View File
@@ -1,3 +1,18 @@
# lxpanel-dtime # lxpanel-dtime
Patch for lxpanel that allows displaying discordian date and erisian time Patch for lxpanel that allows displaying discordian date and erisian time. Preparing **lxpanel 0.11.1** for build and installation, featuring an enhanced Digital Clock: display of unix and erisian time via formatting codes.
Build scripts for Debian 13 and Gentoo. Written by Qwen 3.8 27B.
| Code | Value |
|-------|-------------------------------------------------------------|
| `%eY` | discordian YOLD (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 |
+72
View File
@@ -0,0 +1,72 @@
# lxpanel с эридианскими часами (Digital Clock)
Готовит к сборке и установке **lxpanel 0.11.1** с расширенным Digital Clock:
эридианское (дискордианское) время + unix time через коды форматирования.
## Состав
| Файл | Назначение |
|----------------------|--------------------------------------------------------|
| `build_gentoo.sh` | скрипт: проверка deps → скачивание исходника → патч → сборка → установка |
| `erisian-time.patch` | патч: `plugins/dclock.c` + `po/lxpanel.pot` |
## Быстрый старт (Gentoo)
```sh
tar xf lxpanel-gentoo.tar.gz # если передаёте каталог архивом
cd lxpanel-gentoo
./build_gentoo.sh --deps # установит ebuild'ы зависимостей и соберёт
# далее, если нужно:
./build_gentoo.sh --prefix=/opt/lxpanel # в другую директорию
./build_gentoo.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`.
Executable
+70
View File
@@ -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' ' ')"
+143
View File
@@ -0,0 +1,143 @@
#!/usr/bin/env bash
#
# build_gentoo.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_gentoo.sh # build & install to /usr (default)
# ./build_gentoo.sh --deps # also `emerge` the required ebuilds first
# ./build_gentoo.sh --prefix=/opt/lxpanel
# ./build_gentoo.sh --no-install # build only, do not install
# ./build_gentoo.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"
+264
View File
@@ -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 time and 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; /* discordian 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,
+ _("Additional: %eY — YOLD, %ey — erisian year, %eS — season, %eE — "
+ "season number, %ed — day, %eh:%em:%es — hour:min:sec, %ew — weekday, "
+ "%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 "Additional: %eY — YOLD, %ey — erisian year, %eS — season, %eE — "
+"season number, %ed — day, %eh:%em:%es — hour:min:sec, %ew — weekday, "
+"%ut — unix time"
+msgstr ""
+
#: ../plugins/dclock.c:438
msgid "Action when clicked (default: display calendar)"
msgstr ""