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