1 /* 2 * Grand digital clock for curses compatible terminals 3 * Usage: grdc [-st] [n] -- run for n seconds (default infinity) 4 * grdc -c n -- countdown n seconds 5 * Flags: -c: Countdown timer mode 6 * -s: scroll 7 * -t: output time in 12-hour format 8 * 9 * 10 * modified 10-18-89 for curses (jrl) 11 * 10-18-89 added signal handling 12 * 02-18-02 added countdown timer mode 13 * 14 * modified 03-25-03 for 12 hour option 15 * - Samy Al Bahra <samy@kerneled.com> 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 YBASE 10 26 #define XBASE 10 27 #define XLENGTH 58 28 #define YDEPTH 7 29 30 static struct timespec now; 31 static struct tm *tm; 32 static struct timespec end; 33 34 static short disp[11] = { 35 075557, 011111, 071747, 071717, 055711, 36 074717, 074757, 071111, 075757, 075717, 002020 37 }; 38 static long old[6], next[6], new[6], mask; 39 40 static volatile sig_atomic_t sigtermed; 41 42 static int hascolor = 0; 43 44 static void set(int, int); 45 static void standt(int); 46 static void movto(int, int); 47 static void sighndl(int); 48 static void usage(void) __dead2; 49 50 static void 51 sighndl(int signo) 52 { 53 54 sigtermed = signo; 55 } 56 57 int 58 main(int argc, char *argv[]) 59 { 60 struct timespec delay; 61 time_t prev_sec; 62 long t, a; 63 int i, j, s, k; 64 int n; 65 int ch; 66 bool scrol = false, t12 = false, timer = false; 67 int hour, minute, second; 68 69 while ((ch = getopt(argc, argv, "cst")) != -1) 70 switch (ch) { 71 case 'c': 72 timer = true; 73 break; 74 case 's': 75 scrol = true; 76 break; 77 case 't': 78 t12 = true; 79 break; 80 case '?': 81 default: 82 usage(); 83 /* NOTREACHED */ 84 } 85 argc -= optind; 86 argv += optind; 87 88 if ((argc > 1) || (argc == 0 && timer)) { 89 usage(); 90 /* NOTREACHED */ 91 } 92 93 if (argc > 0) { 94 n = atoi(*argv) + 1; 95 if (n < 1) { 96 warnx("number of seconds is out of range"); 97 usage(); 98 /* NOTREACHED */ 99 } 100 } else 101 n = 0; 102 103 if (timer && n == 0) 104 return(0); 105 106 initscr(); 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 } 148 clock_gettime(CLOCK_REALTIME_FAST, &now); 149 prev_sec = now.tv_sec; 150 if (timer) { 151 end = now; 152 end.tv_sec += n; 153 } 154 do { 155 mask = 0; 156 if (!timer) { 157 tm = localtime(&now.tv_sec); 158 if (t12) { 159 if (tm->tm_hour < 12) { 160 if (tm->tm_hour == 0) 161 tm->tm_hour = 12; 162 mvaddstr(YBASE + 5, XBASE + 52, "AM"); 163 } else { 164 if (tm->tm_hour > 12) 165 tm->tm_hour -= 12; 166 mvaddstr(YBASE + 5, XBASE + 52, "PM"); 167 } 168 } 169 hour = tm->tm_hour; 170 minute = tm->tm_min; 171 second = tm->tm_sec; 172 } else { 173 n = end.tv_sec - now.tv_sec; 174 if (n <= 0) 175 break; 176 hour = (n / 3600) % 100; 177 minute = (n / 60) % 60; 178 second = n % 60; 179 } 180 set(second % 10, 0); 181 set(second / 10, 4); 182 set(minute % 10, 10); 183 set(minute / 10, 14); 184 set(hour % 10, 20); 185 set(hour / 10, 24); 186 set(10, 7); 187 set(10, 17); 188 for(k=0; k<6; k++) { 189 if(scrol) { 190 for(i=0; i<5; i++) 191 new[i] = (new[i]&~mask) | (new[i+1]&mask); 192 new[5] = (new[5]&~mask) | (next[k]&mask); 193 } else 194 new[k] = (new[k]&~mask) | (next[k]&mask); 195 next[k] = 0; 196 for(s=1; s>=0; s--) { 197 standt(s); 198 for(i=0; i<6; i++) { 199 if((a = (new[i]^old[i])&(s ? new : old)[i]) != 0) { 200 for(j=0,t=1<<26; t; t>>=1,j++) { 201 if(a&t) { 202 if(!(a&(t<<1))) { 203 movto(YBASE + i, XBASE + 2*j); 204 } 205 addstr(" "); 206 } 207 } 208 } 209 if(!s) { 210 old[i] = new[i]; 211 } 212 } 213 if(!s) { 214 refresh(); 215 } 216 } 217 } 218 movto(6, 0); 219 refresh(); 220 clock_gettime(CLOCK_REALTIME_FAST, &now); 221 if (now.tv_sec == prev_sec) { 222 if (delay.tv_nsec > 0) { 223 delay.tv_sec = 0; 224 delay.tv_nsec = 1000000000 - now.tv_nsec; 225 } else { 226 delay.tv_sec = 1; 227 delay.tv_nsec = 0; 228 } 229 nanosleep(&delay, NULL); 230 clock_gettime(CLOCK_REALTIME_FAST, &now); 231 } 232 n -= now.tv_sec - prev_sec; 233 prev_sec = now.tv_sec; 234 if (sigtermed) { 235 standend(); 236 clear(); 237 refresh(); 238 endwin(); 239 errx(1, "terminated by signal %d", (int)sigtermed); 240 } 241 } while (n); 242 standend(); 243 clear(); 244 refresh(); 245 endwin(); 246 return(0); 247 } 248 249 static void 250 set(int t, int n) 251 { 252 int i, m; 253 254 m = 7<<n; 255 for(i=0; i<5; i++) { 256 next[i] |= ((disp[t]>>(4-i)*3)&07)<<n; 257 mask |= (next[i]^old[i])&m; 258 } 259 if(mask&m) 260 mask |= m; 261 } 262 263 static void 264 standt(int on) 265 { 266 if (on) { 267 if(hascolor) { 268 attron(COLOR_PAIR(1)); 269 } else { 270 attron(A_STANDOUT); 271 } 272 } else { 273 if(hascolor) { 274 attron(COLOR_PAIR(2)); 275 } else { 276 attroff(A_STANDOUT); 277 } 278 } 279 } 280 281 static void 282 movto(int line, int col) 283 { 284 move(line, col); 285 } 286 287 static void 288 usage(void) 289 { 290 291 (void)fprintf(stderr, "usage: grdc [-st] [n]\n" 292 " grdc -c n\n"); 293 exit(1); 294 } 295