/* $OpenBSD: grdc.c,v 1.37 2022/09/27 03:01:42 deraadt Exp $ */ /* * * Copyright 2002 Amos Shapir. Public domain. * * Grand digital clock for curses compatible terminals * Usage: grdc [-s] [n] -- run for n seconds (default infinity) * Flags: -s: scroll * * modified 10-18-89 for curses (jrl) * 10-18-89 added signal handling */ #include #include #include #include #include #include #include #include #include #include #include #define XLENGTH 58 #define YDEPTH 7 struct timespec now; struct tm *tm; short disp[11] = { 075557, 011111, 071747, 071717, 055711, 074717, 074757, 071111, 075757, 075717, 002020 }; long old[6], next[6], new[6], mask; volatile sig_atomic_t sigalrmed = 0; volatile sig_atomic_t sigtermed = 0; volatile sig_atomic_t sigwinched = 0; int hascolor = 0; void getwinsize(int *, int *); void set(int, int); void standt(int); void __dead usage(void); void sigalrm(int signo) { sigalrmed = signo; } void sighndl(int signo) { sigtermed = signo; } void sigresize(int signo) { sigwinched = signo; } int main(int argc, char *argv[]) { long t, a; int i, j, s, k, rv; int scrol; unsigned int n = 0; struct timespec delay; struct pollfd pfd; const char *errstr; long scroldelay = 50000000; int xbase; int ybase; int wintoosmall; int tz_len = 0; int h, m; int prev_tm_gmtoff; char *tz; tz = getenv("TZ"); if (tz != NULL) tz_len = strlen(tz); scrol = wintoosmall = 0; while ((i = getopt(argc, argv, "sh")) != -1) { switch (i) { case 's': scrol = 1; break; case 'h': default: usage(); } } argv += optind; argc -= optind; if (argc > 1) usage(); if (argc == 1) { n = strtonum(*argv, 1, UINT_MAX, &errstr); if (errstr) { warnx("number of seconds is %s", errstr); usage(); } } initscr(); if (pledge("stdio tty", NULL) == -1) err(1, "pledge"); signal(SIGINT, sighndl); signal(SIGTERM, sighndl); signal(SIGHUP, sighndl); signal(SIGWINCH, sigresize); signal(SIGCONT, sigresize); /* for resizes during suspend */ pfd.fd = STDIN_FILENO; pfd.events = POLLIN; cbreak(); noecho(); hascolor = has_colors(); if (hascolor) { start_color(); init_pair(1, COLOR_BLACK, COLOR_RED); init_pair(2, COLOR_RED, COLOR_BLACK); init_pair(3, COLOR_WHITE, COLOR_BLACK); attrset(COLOR_PAIR(2)); } curs_set(0); sigwinched = 1; /* force initial sizing */ prev_tm_gmtoff = 24 * 3600; /* force initial header printing */ clock_gettime(CLOCK_REALTIME, &now); if (n) { signal(SIGALRM, sigalrm); alarm(n); } do { mask = 0; tm = localtime(&now.tv_sec); set(tm->tm_sec % 10, 0); set(tm->tm_sec / 10, 4); set(tm->tm_min % 10, 10); set(tm->tm_min / 10, 14); set(tm->tm_hour % 10, 20); set(tm->tm_hour / 10, 24); set(10, 7); set(10, 17); /* force repaint if window size changed or DST changed */ if (sigwinched || prev_tm_gmtoff != tm->tm_gmtoff) { sigwinched = 0; wintoosmall = 0; prev_tm_gmtoff = tm->tm_gmtoff; getwinsize(&i, &j); if (i >= XLENGTH + 2) xbase = (i - XLENGTH) / 2; else wintoosmall = 1; if (j >= YDEPTH + 2) ybase = (j - YDEPTH) / 2; else wintoosmall = 1; resizeterm(j, i); clear(); refresh(); if (hascolor && !wintoosmall) { attrset(COLOR_PAIR(3)); mvaddch(ybase - 1, xbase - 1, ACS_ULCORNER); hline(ACS_HLINE, XLENGTH); mvaddch(ybase - 1, xbase + XLENGTH, ACS_URCORNER); mvaddch(ybase + YDEPTH, xbase - 1, ACS_LLCORNER); hline(ACS_HLINE, XLENGTH); mvaddch(ybase + YDEPTH, xbase + XLENGTH, ACS_LRCORNER); move(ybase, xbase - 1); vline(ACS_VLINE, YDEPTH); move(ybase, xbase + XLENGTH); vline(ACS_VLINE, YDEPTH); move(ybase - 1, xbase); h = tm->tm_gmtoff / 3600; m = abs((int)tm->tm_gmtoff % 3600 / 60); if (tz_len > 0 && tz_len <= XLENGTH - strlen("[ () +0000 ]") - strlen(tm->tm_zone)) printw("[ %s (%s) %+2.2d%02d ]", tz, tm->tm_zone, h, m); else printw("[ %s %+2.2d%02d ]", tm->tm_zone, h, m); attrset(COLOR_PAIR(2)); } for (k = 0; k < 6; k++) old[k] = 0; } if (wintoosmall) { move(0, 0); printw("%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec); } else for (k = 0; k < 6; k++) { if (scrol) { for(i = 0; i < 5; i++) new[i] = (new[i] & ~mask) | (new[i + 1] & mask); new[5] = (new[5] & ~mask) | (next[k] & mask); } else new[k] = (new[k] & ~mask) | (next[k] & mask); next[k] = 0; for (s = 1; s >= 0; s--) { standt(s); for (i = 0; i < 6; i++) { if ((a = (new[i] ^ old[i]) & (s ? new : old)[i]) != 0) { for (j = 0, t = 1 << 26; t; t >>= 1, j++) { if (a & t) { if (!(a & (t << 1))) { move(ybase + i + 1, xbase + 2 * (j + 1)); } addstr(" "); } } } if (!s) { old[i] = new[i]; } } if (!s) { refresh(); } } if (scrol && k <= 4) { clock_gettime(CLOCK_REALTIME, &now); delay.tv_sec = 0; delay.tv_nsec = 1000000000 - now.tv_nsec - (4 - k) * scroldelay; if (delay.tv_nsec <= scroldelay && delay.tv_nsec > 0) nanosleep(&delay, NULL); } } move(6, 0); refresh(); clock_gettime(CLOCK_REALTIME, &now); delay.tv_sec = 0; delay.tv_nsec = (1000000000 - now.tv_nsec); /* want scrolling to END on the second */ if (scrol && !wintoosmall) delay.tv_nsec -= 5 * scroldelay; rv = ppoll(&pfd, 1, &delay, NULL); if (rv == 1) { char q = 0; read(STDIN_FILENO, &q, 1); if (q == 'q') sigalrmed = 1; } now.tv_sec++; if (sigtermed) { standend(); clear(); refresh(); endwin(); exit(0); } } while (!sigalrmed); standend(); clear(); refresh(); endwin(); return 0; } void set(int t, int n) { int i, m; m = 7 << n; for (i = 0; i < 5; i++) { next[i] |= ((disp[t] >> (4 - i) * 3) & 07) << n; mask |= (next[i] ^ old[i]) & m; } if (mask & m) mask |= m; } void standt(int on) { if (on) { if (hascolor) { attron(COLOR_PAIR(1)); } else { attron(A_STANDOUT); } } else { if (hascolor) { attron(COLOR_PAIR(2)); } else { attroff(A_STANDOUT); } } } void getwinsize(int *wid, int *ht) { struct winsize size; if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &size) == -1) { *wid = 80; /* Default */ *ht = 24; } else { *wid = size.ws_col; *ht = size.ws_row; } } void __dead usage(void) { fprintf(stderr, "usage: %s [-s] [number]\n", getprogname()); exit(1); }