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