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