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