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