| 1 | /* | 
|---|
| 2 | * Grand digital clock for curses compatible terminals | 
|---|
| 3 | * Usage: gdc [-s] [n]   -- run for n seconds (default infinity) | 
|---|
| 4 | * Flags: -s: scroll | 
|---|
| 5 | * | 
|---|
| 6 | * modified 10-18-89 for curses (jrl) | 
|---|
| 7 | * 10-18-89 added signal handling | 
|---|
| 8 | * | 
|---|
| 9 | * $Id: gdc.c,v 1.26 2005/05/28 21:39:39 tom Exp $ | 
|---|
| 10 | */ | 
|---|
| 11 |  | 
|---|
| 12 | #include <test.priv.h> | 
|---|
| 13 |  | 
|---|
| 14 | #include <time.h> | 
|---|
| 15 |  | 
|---|
| 16 | #define YBASE   10 | 
|---|
| 17 | #define XBASE   10 | 
|---|
| 18 | #define XLENGTH 54 | 
|---|
| 19 | #define YDEPTH  5 | 
|---|
| 20 |  | 
|---|
| 21 | #define PAIR_DIGITS 1 | 
|---|
| 22 | #define PAIR_OTHERS 2 | 
|---|
| 23 | #define PAIR_FRAMES 3 | 
|---|
| 24 |  | 
|---|
| 25 | static short disp[11] = | 
|---|
| 26 | { | 
|---|
| 27 | 075557, 011111, 071747, 071717, 055711, | 
|---|
| 28 | 074717, 074757, 071111, 075757, 075717, 002020 | 
|---|
| 29 | }; | 
|---|
| 30 | static long older[6], next[6], newer[6], mask; | 
|---|
| 31 |  | 
|---|
| 32 | static int sigtermed = 0; | 
|---|
| 33 | static bool redirected = FALSE; | 
|---|
| 34 | static bool hascolor = FALSE; | 
|---|
| 35 |  | 
|---|
| 36 | static RETSIGTYPE | 
|---|
| 37 | sighndl(int signo) | 
|---|
| 38 | { | 
|---|
| 39 | signal(signo, sighndl); | 
|---|
| 40 | sigtermed = signo; | 
|---|
| 41 | if (redirected) { | 
|---|
| 42 | endwin(); | 
|---|
| 43 | ExitProgram(EXIT_FAILURE); | 
|---|
| 44 | } | 
|---|
| 45 | } | 
|---|
| 46 |  | 
|---|
| 47 | static void | 
|---|
| 48 | drawbox(bool scrolling) | 
|---|
| 49 | { | 
|---|
| 50 | chtype bottom[XLENGTH + 1]; | 
|---|
| 51 | int n; | 
|---|
| 52 |  | 
|---|
| 53 | if (hascolor) | 
|---|
| 54 | attrset(COLOR_PAIR(PAIR_FRAMES)); | 
|---|
| 55 |  | 
|---|
| 56 | mvaddch(YBASE - 1, XBASE - 1, ACS_ULCORNER); | 
|---|
| 57 | hline(ACS_HLINE, XLENGTH); | 
|---|
| 58 | mvaddch(YBASE - 1, XBASE + XLENGTH, ACS_URCORNER); | 
|---|
| 59 |  | 
|---|
| 60 | mvaddch(YBASE + YDEPTH, XBASE - 1, ACS_LLCORNER); | 
|---|
| 61 | mvinchnstr(YBASE + YDEPTH, XBASE, bottom, XLENGTH); | 
|---|
| 62 | for (n = 0; n < XLENGTH; n++) { | 
|---|
| 63 | if (!scrolling) | 
|---|
| 64 | bottom[n] &= ~A_COLOR; | 
|---|
| 65 | bottom[n] = ACS_HLINE | (bottom[n] & (A_ATTRIBUTES | A_COLOR)); | 
|---|
| 66 | } | 
|---|
| 67 | mvaddchnstr(YBASE + YDEPTH, XBASE, bottom, XLENGTH); | 
|---|
| 68 | mvaddch(YBASE + YDEPTH, XBASE + XLENGTH, ACS_LRCORNER); | 
|---|
| 69 |  | 
|---|
| 70 | move(YBASE, XBASE - 1); | 
|---|
| 71 | vline(ACS_VLINE, YDEPTH); | 
|---|
| 72 |  | 
|---|
| 73 | move(YBASE, XBASE + XLENGTH); | 
|---|
| 74 | vline(ACS_VLINE, YDEPTH); | 
|---|
| 75 |  | 
|---|
| 76 | if (hascolor) | 
|---|
| 77 | attrset(COLOR_PAIR(PAIR_OTHERS)); | 
|---|
| 78 | } | 
|---|
| 79 |  | 
|---|
| 80 | static void | 
|---|
| 81 | standt(int on) | 
|---|
| 82 | { | 
|---|
| 83 | if (on) { | 
|---|
| 84 | if (hascolor) { | 
|---|
| 85 | attron(COLOR_PAIR(PAIR_DIGITS)); | 
|---|
| 86 | } else { | 
|---|
| 87 | attron(A_STANDOUT); | 
|---|
| 88 | } | 
|---|
| 89 | } else { | 
|---|
| 90 | if (hascolor) { | 
|---|
| 91 | attron(COLOR_PAIR(PAIR_OTHERS)); | 
|---|
| 92 | } else { | 
|---|
| 93 | attroff(A_STANDOUT); | 
|---|
| 94 | } | 
|---|
| 95 | } | 
|---|
| 96 | } | 
|---|
| 97 |  | 
|---|
| 98 | static void | 
|---|
| 99 | set(int t, int n) | 
|---|
| 100 | { | 
|---|
| 101 | int i, m; | 
|---|
| 102 |  | 
|---|
| 103 | m = 7 << n; | 
|---|
| 104 | for (i = 0; i < 5; i++) { | 
|---|
| 105 | next[i] |= ((disp[t] >> ((4 - i) * 3)) & 07) << n; | 
|---|
| 106 | mask |= (next[i] ^ older[i]) & m; | 
|---|
| 107 | } | 
|---|
| 108 | if (mask & m) | 
|---|
| 109 | mask |= m; | 
|---|
| 110 | } | 
|---|
| 111 |  | 
|---|
| 112 | static void | 
|---|
| 113 | usage(void) | 
|---|
| 114 | { | 
|---|
| 115 | static const char *msg[] = | 
|---|
| 116 | { | 
|---|
| 117 | "Usage: gdc [options] [count]" | 
|---|
| 118 | ,"" | 
|---|
| 119 | ,"Options:" | 
|---|
| 120 | ,"  -n  redirect input to /dev/null" | 
|---|
| 121 | ,"  -s  scroll each number into place, rather than flipping" | 
|---|
| 122 | ,"" | 
|---|
| 123 | ,"If you specify a count, gdc runs for that number of seconds" | 
|---|
| 124 | }; | 
|---|
| 125 | unsigned j; | 
|---|
| 126 | for (j = 0; j < SIZEOF(msg); j++) | 
|---|
| 127 | fprintf(stderr, "%s\n", msg[j]); | 
|---|
| 128 | ExitProgram(EXIT_FAILURE); | 
|---|
| 129 | } | 
|---|
| 130 |  | 
|---|
| 131 | int | 
|---|
| 132 | main(int argc, char *argv[]) | 
|---|
| 133 | { | 
|---|
| 134 | time_t now; | 
|---|
| 135 | struct tm *tm; | 
|---|
| 136 | long t, a; | 
|---|
| 137 | int i, j, s, k; | 
|---|
| 138 | int count = 0; | 
|---|
| 139 | FILE *ofp = stdout; | 
|---|
| 140 | FILE *ifp = stdin; | 
|---|
| 141 | bool scrol = FALSE; | 
|---|
| 142 |  | 
|---|
| 143 | setlocale(LC_ALL, ""); | 
|---|
| 144 |  | 
|---|
| 145 | signal(SIGINT, sighndl); | 
|---|
| 146 | signal(SIGTERM, sighndl); | 
|---|
| 147 |  | 
|---|
| 148 | while ((k = getopt(argc, argv, "sn")) != EOF) { | 
|---|
| 149 | switch (k) { | 
|---|
| 150 | case 's': | 
|---|
| 151 | scrol = TRUE; | 
|---|
| 152 | break; | 
|---|
| 153 | case 'n': | 
|---|
| 154 | ifp = fopen("/dev/null", "r"); | 
|---|
| 155 | redirected = TRUE; | 
|---|
| 156 | break; | 
|---|
| 157 | default: | 
|---|
| 158 | usage(); | 
|---|
| 159 | } | 
|---|
| 160 | } | 
|---|
| 161 | if (optind < argc) { | 
|---|
| 162 | count = atoi(argv[optind++]); | 
|---|
| 163 | } | 
|---|
| 164 | if (optind < argc) | 
|---|
| 165 | usage(); | 
|---|
| 166 |  | 
|---|
| 167 | if (redirected) { | 
|---|
| 168 | char *name = getenv("TERM"); | 
|---|
| 169 | if (name == 0 | 
|---|
| 170 | || newterm(name, ofp, ifp) == 0) { | 
|---|
| 171 | fprintf(stderr, "cannot open terminal\n"); | 
|---|
| 172 | ExitProgram(EXIT_FAILURE); | 
|---|
| 173 | } | 
|---|
| 174 |  | 
|---|
| 175 | } else { | 
|---|
| 176 | initscr(); | 
|---|
| 177 | } | 
|---|
| 178 | cbreak(); | 
|---|
| 179 | noecho(); | 
|---|
| 180 | nodelay(stdscr, 1); | 
|---|
| 181 | curs_set(0); | 
|---|
| 182 |  | 
|---|
| 183 | hascolor = has_colors(); | 
|---|
| 184 |  | 
|---|
| 185 | if (hascolor) { | 
|---|
| 186 | int bg = COLOR_BLACK; | 
|---|
| 187 | start_color(); | 
|---|
| 188 | #if HAVE_USE_DEFAULT_COLORS | 
|---|
| 189 | if (use_default_colors() == OK) | 
|---|
| 190 | bg = -1; | 
|---|
| 191 | #endif | 
|---|
| 192 | init_pair(PAIR_DIGITS, COLOR_BLACK, COLOR_RED); | 
|---|
| 193 | init_pair(PAIR_OTHERS, COLOR_RED, bg); | 
|---|
| 194 | init_pair(PAIR_FRAMES, COLOR_WHITE, bg); | 
|---|
| 195 | attrset(COLOR_PAIR(PAIR_OTHERS)); | 
|---|
| 196 | } | 
|---|
| 197 |  | 
|---|
| 198 | restart: | 
|---|
| 199 | for (j = 0; j < 5; j++) | 
|---|
| 200 | older[j] = newer[j] = next[j] = 0; | 
|---|
| 201 |  | 
|---|
| 202 | clear(); | 
|---|
| 203 | drawbox(FALSE); | 
|---|
| 204 |  | 
|---|
| 205 | do { | 
|---|
| 206 | char buf[30]; | 
|---|
| 207 |  | 
|---|
| 208 | time(&now); | 
|---|
| 209 | tm = localtime(&now); | 
|---|
| 210 |  | 
|---|
| 211 | mask = 0; | 
|---|
| 212 | set(tm->tm_sec % 10, 0); | 
|---|
| 213 | set(tm->tm_sec / 10, 4); | 
|---|
| 214 | set(tm->tm_min % 10, 10); | 
|---|
| 215 | set(tm->tm_min / 10, 14); | 
|---|
| 216 | set(tm->tm_hour % 10, 20); | 
|---|
| 217 | set(tm->tm_hour / 10, 24); | 
|---|
| 218 | set(10, 7); | 
|---|
| 219 | set(10, 17); | 
|---|
| 220 |  | 
|---|
| 221 | for (k = 0; k < 6; k++) { | 
|---|
| 222 | if (scrol) { | 
|---|
| 223 | for (i = 0; i < 5; i++) | 
|---|
| 224 | newer[i] = (newer[i] & ~mask) | (newer[i + 1] & mask); | 
|---|
| 225 | newer[5] = (newer[5] & ~mask) | (next[k] & mask); | 
|---|
| 226 | } else | 
|---|
| 227 | newer[k] = (newer[k] & ~mask) | (next[k] & mask); | 
|---|
| 228 | next[k] = 0; | 
|---|
| 229 | for (s = 1; s >= 0; s--) { | 
|---|
| 230 | standt(s); | 
|---|
| 231 | for (i = 0; i < 6; i++) { | 
|---|
| 232 | if ((a = (newer[i] ^ older[i]) & (s ? newer : older)[i]) | 
|---|
| 233 | != 0) { | 
|---|
| 234 | for (j = 0, t = 1 << 26; t; t >>= 1, j++) { | 
|---|
| 235 | if (a & t) { | 
|---|
| 236 | if (!(a & (t << 1))) { | 
|---|
| 237 | move(YBASE + i, XBASE + 2 * j); | 
|---|
| 238 | } | 
|---|
| 239 | addstr("  "); | 
|---|
| 240 | } | 
|---|
| 241 | } | 
|---|
| 242 | } | 
|---|
| 243 | if (!s) { | 
|---|
| 244 | older[i] = newer[i]; | 
|---|
| 245 | } | 
|---|
| 246 | } | 
|---|
| 247 | if (!s) { | 
|---|
| 248 | if (scrol) | 
|---|
| 249 | drawbox(TRUE); | 
|---|
| 250 | refresh(); | 
|---|
| 251 | /* | 
|---|
| 252 | * If we're scrolling, space out the refreshes to fake | 
|---|
| 253 | * movement.  That's 7 frames, or 6 intervals, which would | 
|---|
| 254 | * be 166 msec if we spread it out over a second.  It looks | 
|---|
| 255 | * better (but will work on a slow terminal, e.g., less | 
|---|
| 256 | * than 9600bd) to squeeze that into a half-second, and use | 
|---|
| 257 | * half of 170 msec to ensure that the program doesn't eat | 
|---|
| 258 | * a lot of time when asking what time it is, at the top of | 
|---|
| 259 | * this loop -T.Dickey | 
|---|
| 260 | */ | 
|---|
| 261 | if (scrol) | 
|---|
| 262 | napms(85); | 
|---|
| 263 | } | 
|---|
| 264 | } | 
|---|
| 265 | } | 
|---|
| 266 |  | 
|---|
| 267 | /* this depends on the detailed format of ctime(3) */ | 
|---|
| 268 | (void) strcpy(buf, ctime(&now)); | 
|---|
| 269 | (void) strcpy(buf + 10, buf + 19); | 
|---|
| 270 | mvaddstr(16, 30, buf); | 
|---|
| 271 |  | 
|---|
| 272 | move(6, 0); | 
|---|
| 273 | drawbox(FALSE); | 
|---|
| 274 | refresh(); | 
|---|
| 275 |  | 
|---|
| 276 | /* | 
|---|
| 277 | * If we're not scrolling, wait 1000 msec (1 sec).  Use napms() rather | 
|---|
| 278 | * than sleep() because the latter does odd things on some systems, | 
|---|
| 279 | * e.g., suspending output as well. | 
|---|
| 280 | */ | 
|---|
| 281 | if (scrol) | 
|---|
| 282 | napms(500); | 
|---|
| 283 | else | 
|---|
| 284 | napms(1000); | 
|---|
| 285 |  | 
|---|
| 286 | /* | 
|---|
| 287 | * This is a safe way to check if we're interrupted - making the signal | 
|---|
| 288 | * handler set a flag that we can check.  Since we're running | 
|---|
| 289 | * nodelay(), the wgetch() call returns immediately, and in particular | 
|---|
| 290 | * will return an error if interrupted.  This works only if we can | 
|---|
| 291 | * read from the input, of course. | 
|---|
| 292 | */ | 
|---|
| 293 | switch (wgetch(stdscr)) { | 
|---|
| 294 | case 'q': | 
|---|
| 295 | count = 1; | 
|---|
| 296 | break; | 
|---|
| 297 | case 's': | 
|---|
| 298 | nodelay(stdscr, FALSE); | 
|---|
| 299 | break; | 
|---|
| 300 | case ' ': | 
|---|
| 301 | nodelay(stdscr, TRUE); | 
|---|
| 302 | break; | 
|---|
| 303 | #ifdef KEY_RESIZE | 
|---|
| 304 | case KEY_RESIZE: | 
|---|
| 305 | #endif | 
|---|
| 306 | case '?': | 
|---|
| 307 | goto restart; | 
|---|
| 308 | case ERR: | 
|---|
| 309 | if (sigtermed) { | 
|---|
| 310 | standend(); | 
|---|
| 311 | endwin(); | 
|---|
| 312 | fprintf(stderr, "gdc terminated by signal %d\n", sigtermed); | 
|---|
| 313 | ExitProgram(EXIT_FAILURE); | 
|---|
| 314 | } | 
|---|
| 315 | /* FALLTHRU */ | 
|---|
| 316 | default: | 
|---|
| 317 | continue; | 
|---|
| 318 | } | 
|---|
| 319 | } while (--count); | 
|---|
| 320 | standend(); | 
|---|
| 321 | endwin(); | 
|---|
| 322 | ExitProgram(EXIT_SUCCESS); | 
|---|
| 323 | } | 
|---|