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