---
components/datetime.c | 15 ++++++---
slstatus.c | 71 ++++++++++++++++++++++++++++---------------
2 files changed, 57 insertions(+), 29 deletions(-)
diff --git a/components/datetime.c b/components/datetime.c
index 5b10daf..0b8107f 100644
--- a/components/datetime.c
+++ b/components/datetime.c
_AT_@ -1,5 +1,4 @@
/* See LICENSE file for copyright and license details. */
-#include <stdio.h>
#include <time.h>
#include "../slstatus.h"
_AT_@ -8,10 +7,18 @@
const char *
datetime(const char *fmt)
{
- time_t t;
+ struct timespec ts;
- t = time(NULL);
- if (!strftime(buf, sizeof(buf), fmt, localtime(&t))) {
+ /* Use CLOCK_REALTIME (high-resolution) rather than time(),
+ * which on glibc reads CLOCK_REALTIME_COARSE and lags the
+ * precise clock by up to one jiffy. At a whole-second
+ * boundary the lag truncates the displayed second backwards
+ * by one, making the clock appear to tick a second late. */
+ if (clock_gettime(CLOCK_REALTIME, &ts) < 0) {
+ warn("clock_gettime:");
+ return NULL;
+ }
+ if (!strftime(buf, sizeof(buf), fmt, localtime(&ts.tv_sec))) {
warn("strftime: Result string exceeds buffer size");
return NULL;
}
diff --git a/slstatus.c b/slstatus.c
index 16d88fe..4ae50f8 100644
--- a/slstatus.c
+++ b/slstatus.c
_AT_@ -30,14 +30,6 @@ terminate(const int signo)
done = 1;
}
-static void
-difftimespec(struct timespec *res, struct timespec *a, struct timespec *b)
-{
- res->tv_sec = a->tv_sec - b->tv_sec - (a->tv_nsec < b->tv_nsec);
- res->tv_nsec = a->tv_nsec - b->tv_nsec +
- (a->tv_nsec < b->tv_nsec) * 1E9;
-}
-
static void
usage(void)
{
_AT_@ -48,7 +40,7 @@ int
main(int argc, char *argv[])
{
struct sigaction act;
- struct timespec start, current, diff, intspec, wait;
+ struct timespec next, now;
size_t i, len;
int sflag, ret;
char status[MAXLEN];
_AT_@ -82,10 +74,17 @@ main(int argc, char *argv[])
if (!sflag && !(dpy = XOpenDisplay(NULL)))
die("XOpenDisplay: Failed to open display");
- do {
- if (clock_gettime(CLOCK_MONOTONIC, &start) < 0)
- die("clock_gettime:");
-
+ /* Anchor the first scheduled tick to the next whole-second
+ * boundary on the wall clock. Subsequent ticks use absolute
+ * CLOCK_REALTIME deadlines advanced by `interval`, so the
+ * displayed phase stays locked to wall-clock seconds across
+ * NTP adjustments and suspend/resume. */
+ if (clock_gettime(CLOCK_REALTIME, &next) < 0)
+ die("clock_gettime:");
+ next.tv_nsec = 0;
+ next.tv_sec += 1;
+
+ for (;;) {
status[0] = '\0';
for (i = len = 0; i < LEN(args); i++) {
if (!(res = args[i].func(args[i].args)))
_AT_@ -109,21 +108,43 @@ main(int argc, char *argv[])
XFlush(dpy);
}
- if (!done) {
- if (clock_gettime(CLOCK_MONOTONIC, ¤t) < 0)
- die("clock_gettime:");
- difftimespec(&diff, ¤t, &start);
+ if (done)
+ break;
- intspec.tv_sec = interval / 1000;
- intspec.tv_nsec = (interval % 1000) * 1E6;
- difftimespec(&wait, &intspec, &diff);
+ ret = clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME,
+ &next, NULL);
+ if (ret != 0 && ret != EINTR) {
+ errno = ret;
+ die("clock_nanosleep:");
+ }
- if (wait.tv_sec >= 0 &&
- nanosleep(&wait, NULL) < 0 &&
- errno != EINTR)
- die("nanosleep:");
+ /* Advance the deadline only if it has actually elapsed.
+ * When woken early by a signal (e.g. SIGUSR1 force
+ * refresh), keep `next` so the next iteration sleeps
+ * out the remainder and the wall-clock phase is not
+ * lost. If we have fallen far behind (suspend/resume
+ * or a forwards NTP step), snap to the next aligned
+ * future tick instead of firing a burst of catch-up
+ * renders. */
+ if (clock_gettime(CLOCK_REALTIME, &now) == 0 &&
+ (now.tv_sec > next.tv_sec ||
+ (now.tv_sec == next.tv_sec &&
+ now.tv_nsec >= next.tv_nsec))) {
+ next.tv_sec += interval / 1000;
+ next.tv_nsec += (interval % 1000) * 1000000L;
+ if (next.tv_nsec >= 1000000000L) {
+ next.tv_sec += 1;
+ next.tv_nsec -= 1000000000L;
+ }
+ if (now.tv_sec > next.tv_sec ||
+ (now.tv_sec == next.tv_sec &&
+ now.tv_nsec > next.tv_nsec)) {
+ next = now;
+ next.tv_nsec = 0;
+ next.tv_sec += 1;
+ }
}
- } while (!done);
+ }
if (!sflag) {
XStoreName(dpy, DefaultRootWindow(dpy), NULL);
--
2.47.3
Received on Mon May 04 2026 - 13:44:51 CEST
This archive was generated by hypermail 2.3.0 : Mon May 04 2026 - 13:48:39 CEST