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 25static short disp[11] = 26{ 27 075557, 011111, 071747, 071717, 055711, 28 074717, 074757, 071111, 075757, 075717, 002020 29}; 30static long older[6], next[6], newer[6], mask; 31 32static int sigtermed = 0; 33static bool redirected = FALSE; 34static bool hascolor = FALSE; 35 36static RETSIGTYPE 37sighndl(int signo) 38{ 39 signal(signo, sighndl); 40 sigtermed = signo; 41 if (redirected) { 42 endwin(); 43 ExitProgram(EXIT_FAILURE); 44 } 45} 46 47static void 48drawbox(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 80static void 81standt(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 98static void 99set(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 112static void 113usage(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 131int 132main(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} 324