Created
January 14, 2025 02:23
-
-
Save liweitianux/8537ef7f1e881b76afb25f42ffe04f45 to your computer and use it in GitHub Desktop.
grdc – grand digital clock (curses)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * Copyright (c) 2002 Amos Shapir. | |
| * Public domain. | |
| * | |
| * Grand digital clock for curses compatible terminals | |
| * | |
| * Usage: grdc [-st] [-d msecs] [n] -- run for n seconds (default infinity) | |
| * Flags: -s: scroll (default scroll duration 120msec) | |
| * -t: show time in 12-hour format | |
| * -d msecs: specify scroll duration (implies -s) | |
| * | |
| * modified 10-18-89 for curses (jrl) | |
| * 10-18-89 added signal handling | |
| * 03-23-04 added centering, scroll delay (cap) | |
| * | |
| * $FreeBSD: src/games/grdc/grdc.c,v 1.8.2.1 2001/10/02 11:51:49 ru Exp $ | |
| * | |
| * On Linux: | |
| * $ gcc -O2 -Wall -Wextra -D_GNU_SOURCE $(pkg-config --cflags ncurses) \ | |
| * -o grdc grdc.c $(pkg-config --libs ncurses) | |
| */ | |
| #include <sys/time.h> | |
| #include <err.h> | |
| #include <ncurses.h> | |
| #include <poll.h> | |
| #include <signal.h> | |
| #include <stdlib.h> | |
| #include <time.h> | |
| #include <unistd.h> | |
| #define XLENGTH 58 | |
| #define YDEPTH 7 | |
| #ifndef __dead2 | |
| #define __dead2 __attribute__((noreturn)) | |
| #endif | |
| #ifdef __linux__ | |
| #define CLOCK_REALTIME_FAST CLOCK_REALTIME_COARSE | |
| #define timespecadd(tsp, usp, vsp) \ | |
| do { \ | |
| (vsp)->tv_sec = (tsp)->tv_sec + (usp)->tv_sec; \ | |
| (vsp)->tv_nsec = (tsp)->tv_nsec + (usp)->tv_nsec; \ | |
| if ((vsp)->tv_nsec >= 1000000000L) { \ | |
| (vsp)->tv_sec++; \ | |
| (vsp)->tv_nsec -= 1000000000L; \ | |
| } \ | |
| } while (0) | |
| #endif | |
| static int hascolor = 0; | |
| static int xbase, ybase, xmax, ymax; | |
| static long old[6], next[6], new[6], mask; | |
| static volatile sig_atomic_t sigtermed; | |
| static void cleanup(void); | |
| static void draw_row(int, int); | |
| static void set(int, int); | |
| static void sighndl(int); | |
| static int snooze(long); | |
| static void standt(int); | |
| static void usage(void) __dead2; | |
| static void | |
| sighndl(int signo) | |
| { | |
| sigtermed = signo; | |
| } | |
| int | |
| main(int argc, char **argv) | |
| { | |
| int i, s, k, n, ch; | |
| bool t12, scrol, forever; | |
| long scroll_msecs, delay_msecs; | |
| struct timespec now, scroll_ts; | |
| struct tm *tm; | |
| n = 0; | |
| t12 = false; | |
| forever = true; | |
| scrol = false; | |
| scroll_msecs = 120; | |
| while ((ch = getopt(argc, argv, "d:st")) != -1) { | |
| switch (ch) { | |
| case 'd': | |
| scroll_msecs = atol(optarg); | |
| if (scroll_msecs < 0) | |
| errx(1, "scroll duration may not be negative"); | |
| /* FALLTHROUGH */ | |
| case 's': | |
| scrol = true; | |
| break; | |
| case 't': | |
| t12 = true; | |
| break; | |
| case '?': | |
| default: | |
| usage(); | |
| /* NOTREACHED */ | |
| } | |
| } | |
| argc -= optind; | |
| argv += optind; | |
| if (argc > 1) { | |
| usage(); | |
| /* NOTREACHED */ | |
| } | |
| if (argc > 0) { | |
| n = atoi(*argv); | |
| forever = false; | |
| } | |
| initscr(); | |
| getmaxyx(stdscr, ymax, xmax); | |
| if (ymax < YDEPTH + 2 || xmax < XLENGTH + 4) { | |
| endwin(); | |
| errx(1, "terminal too small"); | |
| } | |
| xbase = (xmax - XLENGTH) / 2 + 2; | |
| ybase = (ymax - YDEPTH) / 2 + 1; | |
| signal(SIGINT, sighndl); | |
| signal(SIGTERM, sighndl); | |
| signal(SIGHUP, sighndl); | |
| cbreak(); | |
| noecho(); | |
| curs_set(0); | |
| 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)); | |
| } | |
| clear(); | |
| refresh(); | |
| if (hascolor) { | |
| attrset(COLOR_PAIR(3)); | |
| mvaddch(ybase - 2, xbase - 3, ACS_ULCORNER); | |
| hline(ACS_HLINE, XLENGTH); | |
| mvaddch(ybase - 2, xbase - 2 + XLENGTH, ACS_URCORNER); | |
| mvaddch(ybase + YDEPTH - 1, xbase - 3, ACS_LLCORNER); | |
| hline(ACS_HLINE, XLENGTH); | |
| mvaddch(ybase + YDEPTH - 1, xbase - 2 + XLENGTH, ACS_LRCORNER); | |
| move(ybase - 1, xbase - 3); | |
| vline(ACS_VLINE, YDEPTH); | |
| move(ybase - 1, xbase - 2 + XLENGTH); | |
| vline(ACS_VLINE, YDEPTH); | |
| attrset(COLOR_PAIR(2)); | |
| refresh(); | |
| } | |
| scroll_ts.tv_sec = scroll_msecs / 1000; | |
| scroll_ts.tv_nsec = 1000000 * (scroll_msecs % 1000); | |
| do { | |
| clock_gettime(CLOCK_REALTIME_FAST, &now); | |
| if (scrol) | |
| timespecadd(&now, &scroll_ts, &now); | |
| tm = localtime(&now.tv_sec); | |
| mask = 0; | |
| set(tm->tm_sec % 10, 0); | |
| set(tm->tm_sec / 10, 4); | |
| set(tm->tm_min % 10, 10); | |
| set(tm->tm_min / 10, 14); | |
| if (t12) { | |
| if (tm->tm_hour == 0) { | |
| tm->tm_hour = 12; | |
| mvaddstr(ybase - 1, xbase, "AM"); | |
| } else if (tm->tm_hour < 12) { | |
| mvaddstr(ybase - 1, xbase, "AM"); | |
| } else if (tm->tm_hour == 12) { | |
| mvaddstr(ybase - 1, xbase, "PM"); | |
| } else { | |
| tm->tm_hour -= 12; | |
| mvaddstr(ybase - 1, xbase, "PM"); | |
| } | |
| } | |
| set(tm->tm_hour % 10, 20); | |
| set(tm->tm_hour / 10, 24); | |
| set(10, 7); | |
| set(10, 17); | |
| for (k = 0; k < 6; k++) { | |
| if (scrol) { | |
| if (snooze(scroll_msecs / 6) == 1) | |
| goto out; | |
| 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++) | |
| draw_row(i, s); | |
| if (!s) { | |
| move(ybase, 0); | |
| refresh(); | |
| } | |
| } | |
| } | |
| move(ybase, 0); | |
| refresh(); | |
| clock_gettime(CLOCK_REALTIME_FAST, &now); | |
| delay_msecs = 1000 - now.tv_nsec / 1000000; | |
| /* want scrolling to end on the second */ | |
| if (scrol) | |
| delay_msecs -= scroll_msecs; | |
| if (delay_msecs > 0) { | |
| if (snooze(delay_msecs) == 1) | |
| goto out; | |
| } | |
| } while (forever ? 1 : --n); | |
| out: | |
| cleanup(); | |
| return (0); | |
| } | |
| static int | |
| snooze(long msecs) | |
| { | |
| static struct pollfd pfd = { | |
| .fd = STDIN_FILENO, | |
| .events = POLLIN, | |
| }; | |
| struct timespec ts; | |
| char c; | |
| int rv; | |
| if (msecs < 1000) { | |
| ts.tv_sec = 0; | |
| ts.tv_nsec = 1000000 * msecs; | |
| } else { | |
| ts.tv_sec = msecs / 1000; | |
| ts.tv_nsec = 1000000 * (msecs % 1000); | |
| } | |
| rv = ppoll(&pfd, 1, &ts, NULL); | |
| if (rv == 1) { | |
| read(pfd.fd, &c, 1); | |
| if (c == 'q') | |
| return (1); | |
| } | |
| if (sigtermed) { | |
| cleanup(); | |
| errx(1, "terminated by signal %d", (int)sigtermed); | |
| } | |
| return (0); | |
| } | |
| static void | |
| cleanup(void) | |
| { | |
| standend(); | |
| clear(); | |
| refresh(); | |
| endwin(); | |
| } | |
| static void | |
| draw_row(int i, int s) | |
| { | |
| long a, t; | |
| int j; | |
| 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, xbase + 2 * j); | |
| } | |
| addstr(" "); | |
| } | |
| } | |
| } | |
| if (!s) { | |
| old[i] = new[i]; | |
| } | |
| } | |
| static void | |
| set(int t, int n) | |
| { | |
| static short disp[11] = { | |
| 075557, 011111, 071747, 071717, 055711, | |
| 074717, 074757, 071111, 075757, 075717, 002020 | |
| }; | |
| 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; | |
| } | |
| static 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); | |
| } | |
| } | |
| } | |
| static void | |
| usage(void) | |
| { | |
| fprintf(stderr, "usage: grdc [-st] [-d msecs] [n]\n"); | |
| exit(1); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment