xref: /openbsd-src/games/grdc/grdc.c (revision e99802964e3755c60b0b400980860f7318d8bfaa)
1*e9980296Sderaadt /*	$OpenBSD: grdc.c,v 1.37 2022/09/27 03:01:42 deraadt Exp $	*/
24acc410cSdownsj /*
3bf56a031Spjanzen  *
4bf56a031Spjanzen  * Copyright 2002 Amos Shapir.  Public domain.
5bf56a031Spjanzen  *
64acc410cSdownsj  * Grand digital clock for curses compatible terminals
74acc410cSdownsj  * Usage: grdc [-s] [n]   -- run for n seconds (default infinity)
84acc410cSdownsj  * Flags: -s: scroll
94acc410cSdownsj  *
104acc410cSdownsj  * modified 10-18-89 for curses (jrl)
114acc410cSdownsj  * 10-18-89 added signal handling
124acc410cSdownsj  */
134acc410cSdownsj 
141b2da923Stedu #include <sys/ioctl.h>
159ea8ec70Smestre 
164acc410cSdownsj #include <curses.h>
172010f3c8Smestre #include <err.h>
18a20cec71Spjanzen #include <limits.h>
198ca82b00Stedu #include <poll.h>
206222ba34Scheloha #include <signal.h>
216222ba34Scheloha #include <stdio.h>
226222ba34Scheloha #include <stdlib.h>
2391219502Sflorian #include <string.h>
246222ba34Scheloha #include <time.h>
254acc410cSdownsj #include <unistd.h>
264acc410cSdownsj 
274acc410cSdownsj #define XLENGTH 58
284acc410cSdownsj #define YDEPTH  7
294acc410cSdownsj 
30a20cec71Spjanzen struct timespec now;
314acc410cSdownsj struct tm *tm;
324acc410cSdownsj 
334acc410cSdownsj short disp[11] = {
344acc410cSdownsj 	075557, 011111, 071747, 071717, 055711,
354acc410cSdownsj 	074717, 074757, 071111, 075757, 075717, 002020
364acc410cSdownsj };
374acc410cSdownsj long old[6], next[6], new[6], mask;
384acc410cSdownsj 
39fb669f89Scheloha volatile sig_atomic_t sigalrmed = 0;
40c48e110cSjsg volatile sig_atomic_t sigtermed = 0;
411b2da923Stedu volatile sig_atomic_t sigwinched = 0;
424acc410cSdownsj 
434acc410cSdownsj int hascolor = 0;
444acc410cSdownsj 
456222ba34Scheloha void getwinsize(int *, int *);
46c72b5b24Smillert void set(int, int);
47c72b5b24Smillert void standt(int);
486222ba34Scheloha void __dead usage(void);
494acc410cSdownsj 
5091764506Sderaadt void
sigalrm(int signo)51fb669f89Scheloha sigalrm(int signo)
52fb669f89Scheloha {
53fb669f89Scheloha 	sigalrmed = signo;
54fb669f89Scheloha }
55fb669f89Scheloha 
56fb669f89Scheloha void
sighndl(int signo)573eb8c9edSjsg sighndl(int signo)
584acc410cSdownsj {
594acc410cSdownsj 	sigtermed = signo;
604acc410cSdownsj }
614acc410cSdownsj 
621b2da923Stedu void
sigresize(int signo)631b2da923Stedu sigresize(int signo)
641b2da923Stedu {
651b2da923Stedu 	sigwinched = signo;
661b2da923Stedu }
671b2da923Stedu 
684acc410cSdownsj int
main(int argc,char * argv[])693eb8c9edSjsg main(int argc, char *argv[])
704acc410cSdownsj {
714acc410cSdownsj 	long t, a;
728ca82b00Stedu 	int i, j, s, k, rv;
7335b20aedSschwarze 	int scrol;
74fb669f89Scheloha 	unsigned int n = 0;
75fb669f89Scheloha 	struct timespec delay;
768ca82b00Stedu 	struct pollfd pfd;
7735b20aedSschwarze 	const char *errstr;
7835b20aedSschwarze 	long scroldelay = 50000000;
797f2aa82dSschwarze 	int xbase;
807f2aa82dSschwarze 	int ybase;
811b2da923Stedu 	int wintoosmall;
8291219502Sflorian 	int tz_len = 0;
8391219502Sflorian 	int h, m;
8491219502Sflorian 	int prev_tm_gmtoff;
8594089c8aSflorian 	char *tz;
8694089c8aSflorian 
8794089c8aSflorian 	tz = getenv("TZ");
8891219502Sflorian 	if (tz != NULL)
8991219502Sflorian 		tz_len = strlen(tz);
904acc410cSdownsj 
911b2da923Stedu 	scrol = wintoosmall = 0;
926222ba34Scheloha 	while ((i = getopt(argc, argv, "sh")) != -1) {
93a20cec71Spjanzen 		switch (i) {
94a20cec71Spjanzen 		case 's':
95a20cec71Spjanzen 			scrol = 1;
96a20cec71Spjanzen 			break;
97a20cec71Spjanzen 		case 'h':
98a20cec71Spjanzen 		default:
99a20cec71Spjanzen 			usage();
100a20cec71Spjanzen 		}
1016222ba34Scheloha 	}
102a20cec71Spjanzen 	argv += optind;
103a20cec71Spjanzen 	argc -= optind;
104a20cec71Spjanzen 
105a20cec71Spjanzen 	if (argc > 1)
106a20cec71Spjanzen 		usage();
107a20cec71Spjanzen 	if (argc == 1) {
108fb669f89Scheloha 		n = strtonum(*argv, 1, UINT_MAX, &errstr);
10935b20aedSschwarze 		if (errstr) {
1109ab6e3d4Stb 			warnx("number of seconds is %s", errstr);
111a20cec71Spjanzen 			usage();
112a20cec71Spjanzen 		}
113a20cec71Spjanzen 	}
114a20cec71Spjanzen 
1151b37764bSpjanzen 	initscr();
1167f2aa82dSschwarze 
117c5ef424aSmestre 	if (pledge("stdio tty", NULL) == -1)
118c5ef424aSmestre 		err(1, "pledge");
119c5ef424aSmestre 
1204acc410cSdownsj 	signal(SIGINT, sighndl);
1214acc410cSdownsj 	signal(SIGTERM, sighndl);
1224acc410cSdownsj 	signal(SIGHUP, sighndl);
1231b2da923Stedu 	signal(SIGWINCH, sigresize);
1241b2da923Stedu 	signal(SIGCONT, sigresize);	/* for resizes during suspend */
1254acc410cSdownsj 
1268ca82b00Stedu 	pfd.fd = STDIN_FILENO;
1278ca82b00Stedu 	pfd.events = POLLIN;
1288ca82b00Stedu 
1294acc410cSdownsj 	cbreak();
1304acc410cSdownsj 	noecho();
1314acc410cSdownsj 
1324acc410cSdownsj 	hascolor = has_colors();
1334acc410cSdownsj 
1344acc410cSdownsj 	if (hascolor) {
1354acc410cSdownsj 		start_color();
1364acc410cSdownsj 		init_pair(1, COLOR_BLACK, COLOR_RED);
1374acc410cSdownsj 		init_pair(2, COLOR_RED, COLOR_BLACK);
1384acc410cSdownsj 		init_pair(3, COLOR_WHITE, COLOR_BLACK);
1394acc410cSdownsj 		attrset(COLOR_PAIR(2));
1404acc410cSdownsj 	}
1414acc410cSdownsj 
142a20cec71Spjanzen 	curs_set(0);
1431b2da923Stedu 	sigwinched = 1;	/* force initial sizing */
14491219502Sflorian 	prev_tm_gmtoff = 24 * 3600; /* force initial header printing */
1451b2da923Stedu 
14677b728daStb 	clock_gettime(CLOCK_REALTIME, &now);
147fb669f89Scheloha 	if (n) {
148fb669f89Scheloha 		signal(SIGALRM, sigalrm);
149fb669f89Scheloha 		alarm(n);
150fb669f89Scheloha 	}
1511b2da923Stedu 	do {
15294089c8aSflorian 		mask = 0;
15394089c8aSflorian 		tm = localtime(&now.tv_sec);
15494089c8aSflorian 		set(tm->tm_sec % 10, 0);
15594089c8aSflorian 		set(tm->tm_sec / 10, 4);
15694089c8aSflorian 		set(tm->tm_min % 10, 10);
15794089c8aSflorian 		set(tm->tm_min / 10, 14);
15894089c8aSflorian 		set(tm->tm_hour % 10, 20);
15994089c8aSflorian 		set(tm->tm_hour / 10, 24);
16094089c8aSflorian 		set(10, 7);
16194089c8aSflorian 		set(10, 17);
16291219502Sflorian 		/* force repaint if window size changed or DST changed */
16391219502Sflorian 		if (sigwinched || prev_tm_gmtoff != tm->tm_gmtoff) {
1641b2da923Stedu 			sigwinched = 0;
1651b2da923Stedu 			wintoosmall = 0;
16691219502Sflorian 			prev_tm_gmtoff = tm->tm_gmtoff;
1671b2da923Stedu 			getwinsize(&i, &j);
1681b2da923Stedu 			if (i >= XLENGTH + 2)
1691b2da923Stedu 				xbase = (i - XLENGTH) / 2;
1701b2da923Stedu 			else
1711b2da923Stedu 				wintoosmall = 1;
1721b2da923Stedu 			if (j >= YDEPTH + 2)
1731b2da923Stedu 				ybase = (j - YDEPTH) / 2;
1741b2da923Stedu 			else
1751b2da923Stedu 				wintoosmall = 1;
1761b2da923Stedu 			resizeterm(j, i);
1774acc410cSdownsj 			clear();
1784acc410cSdownsj 			refresh();
1791b2da923Stedu 			if (hascolor && !wintoosmall) {
1804acc410cSdownsj 				attrset(COLOR_PAIR(3));
1814acc410cSdownsj 
1827f2aa82dSschwarze 				mvaddch(ybase - 1, xbase - 1, ACS_ULCORNER);
1834acc410cSdownsj 				hline(ACS_HLINE, XLENGTH);
1847f2aa82dSschwarze 				mvaddch(ybase - 1, xbase + XLENGTH, ACS_URCORNER);
1854acc410cSdownsj 
1867f2aa82dSschwarze 				mvaddch(ybase + YDEPTH, xbase - 1, ACS_LLCORNER);
1874acc410cSdownsj 				hline(ACS_HLINE, XLENGTH);
1887f2aa82dSschwarze 				mvaddch(ybase + YDEPTH, xbase + XLENGTH, ACS_LRCORNER);
1894acc410cSdownsj 
1907f2aa82dSschwarze 				move(ybase, xbase - 1);
1914acc410cSdownsj 				vline(ACS_VLINE, YDEPTH);
1924acc410cSdownsj 
1937f2aa82dSschwarze 				move(ybase, xbase + XLENGTH);
1944acc410cSdownsj 				vline(ACS_VLINE, YDEPTH);
1954acc410cSdownsj 
19694089c8aSflorian 				move(ybase - 1, xbase);
19791219502Sflorian 
19891219502Sflorian 				h = tm->tm_gmtoff / 3600;
19991219502Sflorian 				m = abs((int)tm->tm_gmtoff % 3600 / 60);
20091219502Sflorian 
20191219502Sflorian 				if (tz_len > 0 && tz_len <= XLENGTH -
20291219502Sflorian 				    strlen("[  () +0000 ]") -
20391219502Sflorian 				    strlen(tm->tm_zone))
20491219502Sflorian 					printw("[ %s (%s) %+2.2d%02d ]", tz,
20591219502Sflorian 					    tm->tm_zone, h, m);
20691219502Sflorian 				else
20791219502Sflorian 					printw("[ %s %+2.2d%02d ]",
20891219502Sflorian 					    tm->tm_zone, h, m);
20994089c8aSflorian 
2104acc410cSdownsj 				attrset(COLOR_PAIR(2));
2114acc410cSdownsj 			}
2121b2da923Stedu 			for (k = 0; k < 6; k++)
2131b2da923Stedu 				old[k] = 0;
2141b2da923Stedu 		}
2151b2da923Stedu 		if (wintoosmall) {
2161b2da923Stedu 			move(0, 0);
2171b2da923Stedu 			printw("%02d:%02d:%02d", tm->tm_hour, tm->tm_min,
2181b2da923Stedu 			    tm->tm_sec);
2191b2da923Stedu 		} else for (k = 0; k < 6; k++) {
2204acc410cSdownsj 			if (scrol) {
2214acc410cSdownsj 				for(i = 0; i < 5; i++)
2224acc410cSdownsj 					new[i] = (new[i] & ~mask) | (new[i + 1] & mask);
2234acc410cSdownsj 				new[5] = (new[5] & ~mask) | (next[k] & mask);
2244acc410cSdownsj 			} else
2254acc410cSdownsj 				new[k] = (new[k] & ~mask) | (next[k] & mask);
2264acc410cSdownsj 			next[k] = 0;
2274acc410cSdownsj 			for (s = 1; s >= 0; s--) {
2284acc410cSdownsj 				standt(s);
2294acc410cSdownsj 				for (i = 0; i < 6; i++) {
2304acc410cSdownsj 					if ((a = (new[i] ^ old[i]) & (s ? new : old)[i]) != 0) {
2314acc410cSdownsj 						for (j = 0, t = 1 << 26; t; t >>= 1, j++) {
2324acc410cSdownsj 							if (a & t) {
2334acc410cSdownsj 								if (!(a & (t << 1))) {
2341b2da923Stedu 									move(ybase + i + 1, xbase + 2 * (j + 1));
2354acc410cSdownsj 								}
2364acc410cSdownsj 								addstr("  ");
2374acc410cSdownsj 							}
2384acc410cSdownsj 						}
2394acc410cSdownsj 					}
2404acc410cSdownsj 					if (!s) {
2414acc410cSdownsj 						old[i] = new[i];
2424acc410cSdownsj 					}
2434acc410cSdownsj 				}
2444acc410cSdownsj 				if (!s) {
2454acc410cSdownsj 					refresh();
2464acc410cSdownsj 				}
2474acc410cSdownsj 			}
24835b20aedSschwarze 			if (scrol && k <= 4) {
24977b728daStb 				clock_gettime(CLOCK_REALTIME, &now);
25035b20aedSschwarze 				delay.tv_sec = 0;
25135b20aedSschwarze 				delay.tv_nsec = 1000000000 - now.tv_nsec
25235b20aedSschwarze 				    - (4 - k) * scroldelay;
25335b20aedSschwarze 				if (delay.tv_nsec <= scroldelay &&
25435b20aedSschwarze 				    delay.tv_nsec > 0)
25535b20aedSschwarze 					nanosleep(&delay, NULL);
25635b20aedSschwarze 			}
2574acc410cSdownsj 		}
2581b2da923Stedu 		move(6, 0);
2594acc410cSdownsj 		refresh();
26077b728daStb 		clock_gettime(CLOCK_REALTIME, &now);
261a3963358Sd 		delay.tv_sec = 0;
262a3963358Sd 		delay.tv_nsec = (1000000000 - now.tv_nsec);
26335b20aedSschwarze 		/* want scrolling to END on the second */
2641b2da923Stedu 		if (scrol && !wintoosmall)
26535b20aedSschwarze 			delay.tv_nsec -= 5 * scroldelay;
2668ca82b00Stedu 		rv = ppoll(&pfd, 1, &delay, NULL);
2678ca82b00Stedu 		if (rv == 1) {
2688ca82b00Stedu 			char q = 0;
2698ca82b00Stedu 			read(STDIN_FILENO, &q, 1);
270fb669f89Scheloha 			if (q == 'q')
271fb669f89Scheloha 				sigalrmed = 1;
2728ca82b00Stedu 		}
273a3963358Sd 		now.tv_sec++;
274a3963358Sd 
2754acc410cSdownsj 		if (sigtermed) {
2764acc410cSdownsj 			standend();
2774acc410cSdownsj 			clear();
2784acc410cSdownsj 			refresh();
2794acc410cSdownsj 			endwin();
280*e9980296Sderaadt 			exit(0);
2814acc410cSdownsj 		}
282fb669f89Scheloha 	} while (!sigalrmed);
2834acc410cSdownsj 	standend();
2844acc410cSdownsj 	clear();
2854acc410cSdownsj 	refresh();
2864acc410cSdownsj 	endwin();
28717641e31Stb 	return 0;
2884acc410cSdownsj }
2894acc410cSdownsj 
2904acc410cSdownsj void
set(int t,int n)2914acc410cSdownsj set(int t, int n)
2924acc410cSdownsj {
2934acc410cSdownsj 	int i, m;
2944acc410cSdownsj 
2954acc410cSdownsj 	m = 7 << n;
2964acc410cSdownsj 	for (i = 0; i < 5; i++) {
2974acc410cSdownsj 		next[i] |= ((disp[t] >> (4 - i) * 3) & 07) << n;
2984acc410cSdownsj 		mask |= (next[i] ^ old[i]) & m;
2994acc410cSdownsj 	}
3004acc410cSdownsj 	if (mask & m)
3014acc410cSdownsj 		mask |= m;
3024acc410cSdownsj }
3034acc410cSdownsj 
3044acc410cSdownsj void
standt(int on)3054acc410cSdownsj standt(int on)
3064acc410cSdownsj {
3074acc410cSdownsj 	if (on) {
3084acc410cSdownsj 		if (hascolor) {
3094acc410cSdownsj 			attron(COLOR_PAIR(1));
3104acc410cSdownsj 		} else {
3114acc410cSdownsj 			attron(A_STANDOUT);
3124acc410cSdownsj 		}
3134acc410cSdownsj 	} else {
3144acc410cSdownsj 		if (hascolor) {
3154acc410cSdownsj 			attron(COLOR_PAIR(2));
3164acc410cSdownsj 		} else {
3174acc410cSdownsj 			attroff(A_STANDOUT);
3184acc410cSdownsj 		}
3194acc410cSdownsj 	}
3204acc410cSdownsj }
3214acc410cSdownsj 
3224acc410cSdownsj void
getwinsize(int * wid,int * ht)3231b2da923Stedu getwinsize(int *wid, int *ht)
3244acc410cSdownsj {
3251b2da923Stedu 	struct winsize size;
3261b2da923Stedu 
327df69c215Sderaadt 	if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &size) == -1) {
3281b2da923Stedu 		*wid = 80;     /* Default */
3291b2da923Stedu 		*ht = 24;
3301b2da923Stedu 	} else {
3311b2da923Stedu 		*wid = size.ws_col;
3321b2da923Stedu 		*ht = size.ws_row;
3331b2da923Stedu 	}
3344acc410cSdownsj }
335a20cec71Spjanzen 
3366222ba34Scheloha void __dead
usage(void)3373eb8c9edSjsg usage(void)
338a20cec71Spjanzen {
3396222ba34Scheloha 	fprintf(stderr, "usage: %s [-s] [number]\n", getprogname());
340a20cec71Spjanzen 	exit(1);
341a20cec71Spjanzen }
342