1 /* $OpenBSD: grdc.c,v 1.33 2020/06/06 17:03:16 cheloha 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 if (pledge("stdio rpath tty", NULL) == -1) 83 err(1, "pledge"); 84 85 scrol = wintoosmall = 0; 86 while ((i = getopt(argc, argv, "sh")) != -1) { 87 switch (i) { 88 case 's': 89 scrol = 1; 90 break; 91 case 'h': 92 default: 93 usage(); 94 } 95 } 96 argv += optind; 97 argc -= optind; 98 99 if (argc > 1) 100 usage(); 101 if (argc == 1) { 102 n = strtonum(*argv, 1, UINT_MAX, &errstr); 103 if (errstr) { 104 warnx("number of seconds is %s", errstr); 105 usage(); 106 } 107 } 108 109 initscr(); 110 111 if (pledge("stdio tty", NULL) == -1) 112 err(1, "pledge"); 113 114 signal(SIGINT, sighndl); 115 signal(SIGTERM, sighndl); 116 signal(SIGHUP, sighndl); 117 signal(SIGWINCH, sigresize); 118 signal(SIGCONT, sigresize); /* for resizes during suspend */ 119 120 pfd.fd = STDIN_FILENO; 121 pfd.events = POLLIN; 122 123 cbreak(); 124 noecho(); 125 126 hascolor = has_colors(); 127 128 if (hascolor) { 129 start_color(); 130 init_pair(1, COLOR_BLACK, COLOR_RED); 131 init_pair(2, COLOR_RED, COLOR_BLACK); 132 init_pair(3, COLOR_WHITE, COLOR_BLACK); 133 attrset(COLOR_PAIR(2)); 134 } 135 136 curs_set(0); 137 sigwinched = 1; /* force initial sizing */ 138 139 clock_gettime(CLOCK_REALTIME, &now); 140 if (n) { 141 signal(SIGALRM, sigalrm); 142 alarm(n); 143 } 144 do { 145 if (sigwinched) { 146 sigwinched = 0; 147 wintoosmall = 0; 148 getwinsize(&i, &j); 149 if (i >= XLENGTH + 2) 150 xbase = (i - XLENGTH) / 2; 151 else 152 wintoosmall = 1; 153 if (j >= YDEPTH + 2) 154 ybase = (j - YDEPTH) / 2; 155 else 156 wintoosmall = 1; 157 resizeterm(j, i); 158 clear(); 159 refresh(); 160 if (hascolor && !wintoosmall) { 161 attrset(COLOR_PAIR(3)); 162 163 mvaddch(ybase - 1, xbase - 1, ACS_ULCORNER); 164 hline(ACS_HLINE, XLENGTH); 165 mvaddch(ybase - 1, xbase + XLENGTH, ACS_URCORNER); 166 167 mvaddch(ybase + YDEPTH, xbase - 1, ACS_LLCORNER); 168 hline(ACS_HLINE, XLENGTH); 169 mvaddch(ybase + YDEPTH, xbase + XLENGTH, ACS_LRCORNER); 170 171 move(ybase, xbase - 1); 172 vline(ACS_VLINE, YDEPTH); 173 174 move(ybase, xbase + XLENGTH); 175 vline(ACS_VLINE, YDEPTH); 176 177 attrset(COLOR_PAIR(2)); 178 } 179 for (k = 0; k < 6; k++) 180 old[k] = 0; 181 } 182 mask = 0; 183 tm = localtime(&now.tv_sec); 184 set(tm->tm_sec % 10, 0); 185 set(tm->tm_sec / 10, 4); 186 set(tm->tm_min % 10, 10); 187 set(tm->tm_min / 10, 14); 188 set(tm->tm_hour % 10, 20); 189 set(tm->tm_hour / 10, 24); 190 set(10, 7); 191 set(10, 17); 192 if (wintoosmall) { 193 move(0, 0); 194 printw("%02d:%02d:%02d", tm->tm_hour, tm->tm_min, 195 tm->tm_sec); 196 } else for (k = 0; k < 6; k++) { 197 if (scrol) { 198 for(i = 0; i < 5; i++) 199 new[i] = (new[i] & ~mask) | (new[i + 1] & mask); 200 new[5] = (new[5] & ~mask) | (next[k] & mask); 201 } else 202 new[k] = (new[k] & ~mask) | (next[k] & mask); 203 next[k] = 0; 204 for (s = 1; s >= 0; s--) { 205 standt(s); 206 for (i = 0; i < 6; i++) { 207 if ((a = (new[i] ^ old[i]) & (s ? new : old)[i]) != 0) { 208 for (j = 0, t = 1 << 26; t; t >>= 1, j++) { 209 if (a & t) { 210 if (!(a & (t << 1))) { 211 move(ybase + i + 1, xbase + 2 * (j + 1)); 212 } 213 addstr(" "); 214 } 215 } 216 } 217 if (!s) { 218 old[i] = new[i]; 219 } 220 } 221 if (!s) { 222 refresh(); 223 } 224 } 225 if (scrol && k <= 4) { 226 clock_gettime(CLOCK_REALTIME, &now); 227 delay.tv_sec = 0; 228 delay.tv_nsec = 1000000000 - now.tv_nsec 229 - (4 - k) * scroldelay; 230 if (delay.tv_nsec <= scroldelay && 231 delay.tv_nsec > 0) 232 nanosleep(&delay, NULL); 233 } 234 } 235 move(6, 0); 236 refresh(); 237 clock_gettime(CLOCK_REALTIME, &now); 238 delay.tv_sec = 0; 239 delay.tv_nsec = (1000000000 - now.tv_nsec); 240 /* want scrolling to END on the second */ 241 if (scrol && !wintoosmall) 242 delay.tv_nsec -= 5 * scroldelay; 243 rv = ppoll(&pfd, 1, &delay, NULL); 244 if (rv == 1) { 245 char q = 0; 246 read(STDIN_FILENO, &q, 1); 247 if (q == 'q') 248 sigalrmed = 1; 249 } 250 now.tv_sec++; 251 252 if (sigtermed) { 253 standend(); 254 clear(); 255 refresh(); 256 endwin(); 257 errx(1, "terminated by signal %d", sigtermed); 258 } 259 } while (!sigalrmed); 260 standend(); 261 clear(); 262 refresh(); 263 endwin(); 264 return 0; 265 } 266 267 void 268 set(int t, int n) 269 { 270 int i, m; 271 272 m = 7 << n; 273 for (i = 0; i < 5; i++) { 274 next[i] |= ((disp[t] >> (4 - i) * 3) & 07) << n; 275 mask |= (next[i] ^ old[i]) & m; 276 } 277 if (mask & m) 278 mask |= m; 279 } 280 281 void 282 standt(int on) 283 { 284 if (on) { 285 if (hascolor) { 286 attron(COLOR_PAIR(1)); 287 } else { 288 attron(A_STANDOUT); 289 } 290 } else { 291 if (hascolor) { 292 attron(COLOR_PAIR(2)); 293 } else { 294 attroff(A_STANDOUT); 295 } 296 } 297 } 298 299 void 300 getwinsize(int *wid, int *ht) 301 { 302 struct winsize size; 303 304 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &size) == -1) { 305 *wid = 80; /* Default */ 306 *ht = 24; 307 } else { 308 *wid = size.ws_col; 309 *ht = size.ws_row; 310 } 311 } 312 313 void __dead 314 usage(void) 315 { 316 fprintf(stderr, "usage: %s [-s] [number]\n", getprogname()); 317 exit(1); 318 } 319