1 /* $OpenBSD: grdc.c,v 1.34 2021/10/23 11:22:49 mestre Exp $ */ 2 /* 3 * 4 * Copyright 2002 Amos Shapir. Public domain. 5 * 6 * Grand digital clock for curses compatible terminals 7 * Usage: grdc [-s] [n] -- run for n seconds (default infinity) 8 * Flags: -s: scroll 9 * 10 * modified 10-18-89 for curses (jrl) 11 * 10-18-89 added signal handling 12 */ 13 14 #include <sys/ioctl.h> 15 16 #include <curses.h> 17 #include <err.h> 18 #include <limits.h> 19 #include <poll.h> 20 #include <signal.h> 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <time.h> 24 #include <unistd.h> 25 26 #define XLENGTH 58 27 #define YDEPTH 7 28 29 struct timespec now; 30 struct tm *tm; 31 32 short disp[11] = { 33 075557, 011111, 071747, 071717, 055711, 34 074717, 074757, 071111, 075757, 075717, 002020 35 }; 36 long old[6], next[6], new[6], mask; 37 38 volatile sig_atomic_t sigalrmed = 0; 39 volatile sig_atomic_t sigtermed = 0; 40 volatile sig_atomic_t sigwinched = 0; 41 42 int hascolor = 0; 43 44 void getwinsize(int *, int *); 45 void set(int, int); 46 void standt(int); 47 void __dead usage(void); 48 49 void 50 sigalrm(int signo) 51 { 52 sigalrmed = signo; 53 } 54 55 void 56 sighndl(int signo) 57 { 58 sigtermed = signo; 59 } 60 61 void 62 sigresize(int signo) 63 { 64 sigwinched = signo; 65 } 66 67 int 68 main(int argc, char *argv[]) 69 { 70 long t, a; 71 int i, j, s, k, rv; 72 int scrol; 73 unsigned int n = 0; 74 struct timespec delay; 75 struct pollfd pfd; 76 const char *errstr; 77 long scroldelay = 50000000; 78 int xbase; 79 int ybase; 80 int wintoosmall; 81 82 scrol = wintoosmall = 0; 83 while ((i = getopt(argc, argv, "sh")) != -1) { 84 switch (i) { 85 case 's': 86 scrol = 1; 87 break; 88 case 'h': 89 default: 90 usage(); 91 } 92 } 93 argv += optind; 94 argc -= optind; 95 96 if (argc > 1) 97 usage(); 98 if (argc == 1) { 99 n = strtonum(*argv, 1, UINT_MAX, &errstr); 100 if (errstr) { 101 warnx("number of seconds is %s", errstr); 102 usage(); 103 } 104 } 105 106 initscr(); 107 108 if (pledge("stdio tty", NULL) == -1) 109 err(1, "pledge"); 110 111 signal(SIGINT, sighndl); 112 signal(SIGTERM, sighndl); 113 signal(SIGHUP, sighndl); 114 signal(SIGWINCH, sigresize); 115 signal(SIGCONT, sigresize); /* for resizes during suspend */ 116 117 pfd.fd = STDIN_FILENO; 118 pfd.events = POLLIN; 119 120 cbreak(); 121 noecho(); 122 123 hascolor = has_colors(); 124 125 if (hascolor) { 126 start_color(); 127 init_pair(1, COLOR_BLACK, COLOR_RED); 128 init_pair(2, COLOR_RED, COLOR_BLACK); 129 init_pair(3, COLOR_WHITE, COLOR_BLACK); 130 attrset(COLOR_PAIR(2)); 131 } 132 133 curs_set(0); 134 sigwinched = 1; /* force initial sizing */ 135 136 clock_gettime(CLOCK_REALTIME, &now); 137 if (n) { 138 signal(SIGALRM, sigalrm); 139 alarm(n); 140 } 141 do { 142 if (sigwinched) { 143 sigwinched = 0; 144 wintoosmall = 0; 145 getwinsize(&i, &j); 146 if (i >= XLENGTH + 2) 147 xbase = (i - XLENGTH) / 2; 148 else 149 wintoosmall = 1; 150 if (j >= YDEPTH + 2) 151 ybase = (j - YDEPTH) / 2; 152 else 153 wintoosmall = 1; 154 resizeterm(j, i); 155 clear(); 156 refresh(); 157 if (hascolor && !wintoosmall) { 158 attrset(COLOR_PAIR(3)); 159 160 mvaddch(ybase - 1, xbase - 1, ACS_ULCORNER); 161 hline(ACS_HLINE, XLENGTH); 162 mvaddch(ybase - 1, xbase + XLENGTH, ACS_URCORNER); 163 164 mvaddch(ybase + YDEPTH, xbase - 1, ACS_LLCORNER); 165 hline(ACS_HLINE, XLENGTH); 166 mvaddch(ybase + YDEPTH, xbase + XLENGTH, ACS_LRCORNER); 167 168 move(ybase, xbase - 1); 169 vline(ACS_VLINE, YDEPTH); 170 171 move(ybase, xbase + XLENGTH); 172 vline(ACS_VLINE, YDEPTH); 173 174 attrset(COLOR_PAIR(2)); 175 } 176 for (k = 0; k < 6; k++) 177 old[k] = 0; 178 } 179 mask = 0; 180 tm = localtime(&now.tv_sec); 181 set(tm->tm_sec % 10, 0); 182 set(tm->tm_sec / 10, 4); 183 set(tm->tm_min % 10, 10); 184 set(tm->tm_min / 10, 14); 185 set(tm->tm_hour % 10, 20); 186 set(tm->tm_hour / 10, 24); 187 set(10, 7); 188 set(10, 17); 189 if (wintoosmall) { 190 move(0, 0); 191 printw("%02d:%02d:%02d", tm->tm_hour, tm->tm_min, 192 tm->tm_sec); 193 } else for (k = 0; k < 6; k++) { 194 if (scrol) { 195 for(i = 0; i < 5; i++) 196 new[i] = (new[i] & ~mask) | (new[i + 1] & mask); 197 new[5] = (new[5] & ~mask) | (next[k] & mask); 198 } else 199 new[k] = (new[k] & ~mask) | (next[k] & mask); 200 next[k] = 0; 201 for (s = 1; s >= 0; s--) { 202 standt(s); 203 for (i = 0; i < 6; i++) { 204 if ((a = (new[i] ^ old[i]) & (s ? new : old)[i]) != 0) { 205 for (j = 0, t = 1 << 26; t; t >>= 1, j++) { 206 if (a & t) { 207 if (!(a & (t << 1))) { 208 move(ybase + i + 1, xbase + 2 * (j + 1)); 209 } 210 addstr(" "); 211 } 212 } 213 } 214 if (!s) { 215 old[i] = new[i]; 216 } 217 } 218 if (!s) { 219 refresh(); 220 } 221 } 222 if (scrol && k <= 4) { 223 clock_gettime(CLOCK_REALTIME, &now); 224 delay.tv_sec = 0; 225 delay.tv_nsec = 1000000000 - now.tv_nsec 226 - (4 - k) * scroldelay; 227 if (delay.tv_nsec <= scroldelay && 228 delay.tv_nsec > 0) 229 nanosleep(&delay, NULL); 230 } 231 } 232 move(6, 0); 233 refresh(); 234 clock_gettime(CLOCK_REALTIME, &now); 235 delay.tv_sec = 0; 236 delay.tv_nsec = (1000000000 - now.tv_nsec); 237 /* want scrolling to END on the second */ 238 if (scrol && !wintoosmall) 239 delay.tv_nsec -= 5 * scroldelay; 240 rv = ppoll(&pfd, 1, &delay, NULL); 241 if (rv == 1) { 242 char q = 0; 243 read(STDIN_FILENO, &q, 1); 244 if (q == 'q') 245 sigalrmed = 1; 246 } 247 now.tv_sec++; 248 249 if (sigtermed) { 250 standend(); 251 clear(); 252 refresh(); 253 endwin(); 254 errx(1, "terminated by signal %d", sigtermed); 255 } 256 } while (!sigalrmed); 257 standend(); 258 clear(); 259 refresh(); 260 endwin(); 261 return 0; 262 } 263 264 void 265 set(int t, int n) 266 { 267 int i, m; 268 269 m = 7 << n; 270 for (i = 0; i < 5; i++) { 271 next[i] |= ((disp[t] >> (4 - i) * 3) & 07) << n; 272 mask |= (next[i] ^ old[i]) & m; 273 } 274 if (mask & m) 275 mask |= m; 276 } 277 278 void 279 standt(int on) 280 { 281 if (on) { 282 if (hascolor) { 283 attron(COLOR_PAIR(1)); 284 } else { 285 attron(A_STANDOUT); 286 } 287 } else { 288 if (hascolor) { 289 attron(COLOR_PAIR(2)); 290 } else { 291 attroff(A_STANDOUT); 292 } 293 } 294 } 295 296 void 297 getwinsize(int *wid, int *ht) 298 { 299 struct winsize size; 300 301 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &size) == -1) { 302 *wid = 80; /* Default */ 303 *ht = 24; 304 } else { 305 *wid = size.ws_col; 306 *ht = size.ws_row; 307 } 308 } 309 310 void __dead 311 usage(void) 312 { 313 fprintf(stderr, "usage: %s [-s] [number]\n", getprogname()); 314 exit(1); 315 } 316