aboutsummaryrefslogblamecommitdiff
path: root/usr.bin/grdc/grdc.c
blob: 07d12f5a7fb07824428fbe2b8c68d57e80cc8c78 (plain) (tree)
1
2
3
4
5
6
7
8

                                                      
                                                                  


                                                 

                                           


                                     
                                      
  

                                          

   
                
                    
                   
                   
                 
                   





                  

                           
                           
 
                         


                                                      
                                          
 
                                       
 
                        
 



                            
                                
 
           
                  
 

                          


   
                            
 
                              
                        



                       

                                                       
 
                                                      
                     


                             
                 
                             
                      
                 
                           
                      







                                
                                                 



                                
                       
                                    





                                                                   
                      
 


                            







                                
                    
 

                                
                       





                                                       
 

                  
 
                       

















                                                                                
                                                 
                              



                                

                         











                                                                              
                         









                                                    
                 





                                     

































                                                                                                      

                                                         

                                                 
                                                                         




                                                  
                                                                 
                 

                                           




                                   
                                                                           
                 
                    






                   
           

                 
                 









                                                      
           





                                              

                                           








                                              
           




                        
           


           

                                                       

                
/*
 * Grand digital clock for curses compatible terminals
 * Usage: grdc [-st] [n]   -- run for n seconds (default infinity)
 *        grdc -c n        -- countdown n seconds
 * Flags: -c: Countdown timer mode
 *        -s: scroll
 *        -t: output time in 12-hour format
 *
 *
 * modified 10-18-89 for curses (jrl)
 * 10-18-89 added signal handling
 * 02-18-02 added countdown timer mode
 *
 * modified 03-25-03 for 12 hour option
 *     - Samy Al Bahra <samy@kerneled.com>
 */

#include <err.h>
#include <ncurses.h>
#include <signal.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>

#define YBASE	10
#define XBASE	10
#define XLENGTH 58
#define YDEPTH  7

static struct timespec now;
static struct tm *tm;
static struct timespec end;

static short disp[11] = {
	075557, 011111, 071747, 071717, 055711,
	074717, 074757, 071111, 075757, 075717, 002020
};
static long old[6], next[6], new[6], mask;

static volatile sig_atomic_t sigtermed;

static int hascolor = 0;

static void set(int, int);
static void standt(int);
static void movto(int, int);
static void sighndl(int);
static void usage(void) __dead2;

static void
sighndl(int signo)
{

	sigtermed = signo;
}

int
main(int argc, char *argv[])
{
	struct timespec delay;
	time_t prev_sec;
	long t, a;
	int i, j, s, k;
	int n;
	int ch;
	bool scrol = false, t12 = false, timer = false;
	int hour, minute, second;

	while ((ch = getopt(argc, argv, "cst")) != -1)
	switch (ch) {
	case 'c':
		timer = true;
		break;
	case 's':
		scrol = true;
		break;
	case 't':
		t12 = true;
		break;
	case '?':
	default:
		usage();
		/* NOTREACHED */
	}
	argc -= optind;
	argv += optind;

	if ((argc > 1) || (argc == 0 && timer)) {
		usage();
		/* NOTREACHED */
	}

	if (argc > 0) {
		n = atoi(*argv) + 1;
		if (n < 1) {
			warnx("number of seconds is out of range");
			usage();
			/* NOTREACHED */
		}
	} else
		n = 0;

	if (timer && n == 0)
		return(0);

	initscr();

	signal(SIGINT,sighndl);
	signal(SIGTERM,sighndl);
	signal(SIGHUP,sighndl);

	cbreak();
	noecho();
	curs_set(0);

	hascolor = has_colors();

	if (hascolor) {
		start_color();
		init_pair(1, COLOR_BLACK, COLOR_RED);
		init_pair(2, COLOR_RED, COLOR_BLACK);
		init_pair(3, COLOR_WHITE, COLOR_BLACK);
		attrset(COLOR_PAIR(2));
	}

	clear();
	refresh();

	if (hascolor) {
		attrset(COLOR_PAIR(3));

		mvaddch(YBASE - 2,  XBASE - 3, ACS_ULCORNER);
		hline(ACS_HLINE, XLENGTH);
		mvaddch(YBASE - 2,  XBASE - 2 + XLENGTH, ACS_URCORNER);

		mvaddch(YBASE + YDEPTH - 1,  XBASE - 3, ACS_LLCORNER);
		hline(ACS_HLINE, XLENGTH);
		mvaddch(YBASE + YDEPTH - 1,  XBASE - 2 + XLENGTH, ACS_LRCORNER);

		move(YBASE - 1,  XBASE - 3);
		vline(ACS_VLINE, YDEPTH);

		move(YBASE - 1,  XBASE - 2 + XLENGTH);
		vline(ACS_VLINE, YDEPTH);

		attrset(COLOR_PAIR(2));
	}
	clock_gettime(CLOCK_REALTIME_FAST, &now);
	prev_sec = now.tv_sec;
	if (timer) {
		end = now;
		end.tv_sec += n;
	}
	do {
		mask = 0;
		if (!timer) {
			tm = localtime(&now.tv_sec);
			if (t12) {
				if (tm->tm_hour < 12) {
					if (tm->tm_hour == 0)
						tm->tm_hour = 12;
					mvaddstr(YBASE + 5, XBASE + 52, "AM");
				} else {
					if (tm->tm_hour > 12)
						tm->tm_hour -= 12;
					mvaddstr(YBASE + 5, XBASE + 52, "PM");
				}
			}
			hour = tm->tm_hour;
			minute = tm->tm_min;
			second = tm->tm_sec;
		} else {
			n = end.tv_sec - now.tv_sec;
			if (n <= 0)
				break;
			hour = (n / 3600) % 100;
			minute = (n / 60) % 60;
			second = n % 60;
		}
		set(second % 10, 0);
		set(second / 10, 4);
		set(minute % 10, 10);
		set(minute / 10, 14);
		set(hour % 10, 20);
		set(hour / 10, 24);
		set(10, 7);
		set(10, 17);
		for(k=0; k<6; k++) {
			if(scrol) {
				for(i=0; i<5; i++)
					new[i] = (new[i]&~mask) | (new[i+1]&mask);
				new[5] = (new[5]&~mask) | (next[k]&mask);
			} else
				new[k] = (new[k]&~mask) | (next[k]&mask);
			next[k] = 0;
			for(s=1; s>=0; s--) {
				standt(s);
				for(i=0; i<6; i++) {
					if((a = (new[i]^old[i])&(s ? new : old)[i]) != 0) {
						for(j=0,t=1<<26; t; t>>=1,j++) {
							if(a&t) {
								if(!(a&(t<<1))) {
									movto(YBASE + i, XBASE + 2*j);
								}
								addstr("  ");
							}
						}
					}
					if(!s) {
						old[i] = new[i];
					}
				}
				if(!s) {
					refresh();
				}
			}
		}
		movto(6, 0);
		refresh();
		clock_gettime(CLOCK_REALTIME_FAST, &now);
		if (now.tv_sec == prev_sec) {
			if (delay.tv_nsec > 0) {
				delay.tv_sec = 0;
				delay.tv_nsec = 1000000000 - now.tv_nsec;
			} else {
				delay.tv_sec = 1;
				delay.tv_nsec = 0;
			}
			nanosleep(&delay, NULL);
			clock_gettime(CLOCK_REALTIME_FAST, &now);
		}
		n -= now.tv_sec - prev_sec;
		prev_sec = now.tv_sec;
		if (sigtermed) {
			standend();
			clear();
			refresh();
			endwin();
			errx(1, "terminated by signal %d", (int)sigtermed);
		}
	} while (n);
	standend();
	clear();
	refresh();
	endwin();
	return(0);
}

static void
set(int t, int n)
{
	int i, m;

	m = 7<<n;
	for(i=0; i<5; i++) {
		next[i] |= ((disp[t]>>(4-i)*3)&07)<<n;
		mask |= (next[i]^old[i])&m;
	}
	if(mask&m)
		mask |= m;
}

static void
standt(int on)
{
	if (on) {
		if(hascolor) {
			attron(COLOR_PAIR(1));
		} else {
			attron(A_STANDOUT);
		}
	} else {
		if(hascolor) {
			attron(COLOR_PAIR(2));
		} else {
			attroff(A_STANDOUT);
		}
	}
}

static void
movto(int line, int col)
{
	move(line, col);
}

static void
usage(void)
{

	(void)fprintf(stderr, "usage: grdc [-st] [n]\n"
	    "      grdc -c n\n");
	exit(1);
}