/* * TTY-CLOCK Main file. * Copyright © 2009-2018 tty-clock contributors * Copyright © 2008 Martin Duquesnoy * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of the nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "ttyclock.h" void init(void) { struct sigaction sig; ttyclock->bg = COLOR_BLACK; /* Init ncurses */ if (ttyclock->tty) { FILE *ftty = fopen(ttyclock->tty, "r+"); if (!ftty) { fprintf(stderr, "tty-clock: error: '%s' couldn't be opened: %s.\n", ttyclock->tty, strerror(errno)); exit(EXIT_FAILURE); } ttyclock->ttyscr = newterm(NULL, ftty, ftty); assert(ttyclock->ttyscr != NULL); set_term(ttyclock->ttyscr); } else initscr(); cbreak(); noecho(); keypad(stdscr, True); start_color(); curs_set(False); clear(); /* Init default terminal color */ if(use_default_colors() == OK) ttyclock->bg = -1; /* Init color pair */ init_pair(0, ttyclock->bg, ttyclock->bg); init_pair(1, ttyclock->bg, ttyclock->option.color); init_pair(2, ttyclock->option.color, ttyclock->bg); // init_pair(0, ttyclock->bg, ttyclock->bg); // init_pair(1, ttyclock->bg, ttyclock->option.color); // init_pair(2, ttyclock->option.color, ttyclock->bg); refresh(); /* Init signal handler */ sig.sa_handler = signal_handler; sig.sa_flags = 0; sigaction(SIGWINCH, &sig, NULL); sigaction(SIGTERM, &sig, NULL); sigaction(SIGINT, &sig, NULL); sigaction(SIGSEGV, &sig, NULL); /* Init global struct */ ttyclock->running = True; if(!ttyclock->geo.x) ttyclock->geo.x = 0; if(!ttyclock->geo.y) ttyclock->geo.y = 0; if(!ttyclock->geo.a) ttyclock->geo.a = 1; if(!ttyclock->geo.b) ttyclock->geo.b = 1; ttyclock->geo.w = (ttyclock->option.second) ? SECFRAMEW : NORMFRAMEW; ttyclock->geo.h = 7; ttyclock->tm = localtime(&(ttyclock->lt)); if(ttyclock->option.utc) { ttyclock->tm = gmtime(&(ttyclock->lt)); } ttyclock->lt = time(NULL); update_hour(); /* Create clock win */ ttyclock->framewin = newwin(ttyclock->geo.h, ttyclock->geo.w, ttyclock->geo.x, ttyclock->geo.y); if(ttyclock->option.box) { box(ttyclock->framewin, 0, 0); } if (ttyclock->option.bold) { wattron(ttyclock->framewin, A_BLINK); } /* Create the date win */ ttyclock->datewin = newwin(DATEWINH, strlen(ttyclock->date.datestr) + 2, ttyclock->geo.x + ttyclock->geo.h - 1, ttyclock->geo.y + (ttyclock->geo.w / 2) - (strlen(ttyclock->date.datestr) / 2) - 1); if(ttyclock->option.box && ttyclock->option.date) { box(ttyclock->datewin, 0, 0); } clearok(ttyclock->datewin, True); set_center(ttyclock->option.center); nodelay(stdscr, True); if (ttyclock->option.date) { wrefresh(ttyclock->datewin); } wrefresh(ttyclock->framewin); return; } void signal_handler(int signal) { switch(signal) { case SIGWINCH: endwin(); init(); break; /* Interruption signal */ case SIGINT: case SIGTERM: ttyclock->running = False; /* Segmentation fault signal */ break; case SIGSEGV: endwin(); fprintf(stderr, "Segmentation fault.\n"); exit(EXIT_FAILURE); break; } return; } void cleanup(void) { if (ttyclock->ttyscr) delscreen(ttyclock->ttyscr); if (ttyclock && ttyclock->tty) free(ttyclock->tty); if (ttyclock && ttyclock->option.format) free(ttyclock->option.format); if (ttyclock) free(ttyclock); } void update_hour(void) { int ihour; char tmpstr[128]; ttyclock->lt = time(NULL); ttyclock->tm = localtime(&(ttyclock->lt)); if(ttyclock->option.utc) { ttyclock->tm = gmtime(&(ttyclock->lt)); } ihour = ttyclock->tm->tm_hour; if(ttyclock->option.twelve) ttyclock->meridiem = ((ihour >= 12) ? PMSIGN : AMSIGN); else ttyclock->meridiem = "\0"; /* Manage hour for twelve mode */ ihour = ((ttyclock->option.twelve && ihour > 12) ? (ihour - 12) : ihour); ihour = ((ttyclock->option.twelve && !ihour) ? 12 : ihour); /* Set hour */ ttyclock->date.hour[0] = ihour / 10; ttyclock->date.hour[1] = ihour % 10; /* Set minutes */ ttyclock->date.minute[0] = ttyclock->tm->tm_min / 10; ttyclock->date.minute[1] = ttyclock->tm->tm_min % 10; /* Set date string */ strftime(tmpstr, sizeof(tmpstr), ttyclock->option.format, ttyclock->tm); sprintf(ttyclock->date.datestr, "%s%s", tmpstr, ttyclock->meridiem); /* Set seconds */ ttyclock->date.second[0] = ttyclock->tm->tm_sec / 10; ttyclock->date.second[1] = ttyclock->tm->tm_sec % 10; return; } void draw_number(int n, int x, int y) { int i, sy = y; for(i = 0; i < 30; ++i, ++sy) { if(sy == y + 6) { sy = y; ++x; } if (ttyclock->option.bold) wattron(ttyclock->framewin, A_BLINK); else wattroff(ttyclock->framewin, A_BLINK); wbkgdset(ttyclock->framewin, COLOR_PAIR(number[n][i/2])); mvwaddch(ttyclock->framewin, x, sy, ' '); } wrefresh(ttyclock->framewin); return; } void draw_clock(void) { /* Draw hour numbers */ draw_number(ttyclock->date.hour[0], 1, 1); draw_number(ttyclock->date.hour[1], 1, 8); chtype dotcolor = COLOR_PAIR(1); if (ttyclock->option.blink && time(NULL) % 2 == 0) dotcolor = COLOR_PAIR(2); /* 2 dot for number separation */ wbkgdset(ttyclock->framewin, dotcolor); mvwaddstr(ttyclock->framewin, 2, 16, " "); mvwaddstr(ttyclock->framewin, 4, 16, " "); /* Draw minute numbers */ draw_number(ttyclock->date.minute[0], 1, 20); draw_number(ttyclock->date.minute[1], 1, 27); /* Draw the date */ if (ttyclock->option.bold) wattron(ttyclock->datewin, A_BOLD); else wattroff(ttyclock->datewin, A_BOLD); if (ttyclock->option.date) { wbkgdset(ttyclock->datewin, (COLOR_PAIR(2))); mvwprintw(ttyclock->datewin, (DATEWINH / 2), 1, ttyclock->date.datestr); wrefresh(ttyclock->datewin); } /* Draw second if the option is enable */ if(ttyclock->option.second) { /* Again 2 dot for number separation */ wbkgdset(ttyclock->framewin, dotcolor); mvwaddstr(ttyclock->framewin, 2, NORMFRAMEW, " "); mvwaddstr(ttyclock->framewin, 4, NORMFRAMEW, " "); /* Draw second numbers */ draw_number(ttyclock->date.second[0], 1, 39); draw_number(ttyclock->date.second[1], 1, 46); } return; } void clock_move(int x, int y, int w, int h) { /* Erase border for a clean move */ wbkgdset(ttyclock->framewin, COLOR_PAIR(0)); wborder(ttyclock->framewin, ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '); werase(ttyclock->framewin); wrefresh(ttyclock->framewin); if (ttyclock->option.date) { wbkgdset(ttyclock->datewin, COLOR_PAIR(0)); wborder(ttyclock->datewin, ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '); werase(ttyclock->datewin); wrefresh(ttyclock->datewin); } /* Frame win move */ mvwin(ttyclock->framewin, (ttyclock->geo.x = x), (ttyclock->geo.y = y)); wresize(ttyclock->framewin, (ttyclock->geo.h = h), (ttyclock->geo.w = w)); /* Date win move */ if (ttyclock->option.date) { mvwin(ttyclock->datewin, ttyclock->geo.x + ttyclock->geo.h - 1, ttyclock->geo.y + (ttyclock->geo.w / 2) - (strlen(ttyclock->date.datestr) / 2) - 1); wresize(ttyclock->datewin, DATEWINH, strlen(ttyclock->date.datestr) + 2); if (ttyclock->option.box) { box(ttyclock->datewin, 0, 0); } } if (ttyclock->option.box) { box(ttyclock->framewin, 0, 0); } wrefresh(ttyclock->framewin); wrefresh(ttyclock->datewin); return; } /* Useless but fun :) */ void clock_rebound(void) { if(!ttyclock->option.rebound) return; if(ttyclock->geo.x < 1) ttyclock->geo.a = 1; if(ttyclock->geo.x > (LINES - ttyclock->geo.h - DATEWINH)) ttyclock->geo.a = -1; if(ttyclock->geo.y < 1) ttyclock->geo.b = 1; if(ttyclock->geo.y > (COLS - ttyclock->geo.w - 1)) ttyclock->geo.b = -1; clock_move(ttyclock->geo.x + ttyclock->geo.a, ttyclock->geo.y + ttyclock->geo.b, ttyclock->geo.w, ttyclock->geo.h); return; } void set_second(void) { int new_w = (((ttyclock->option.second = !ttyclock->option.second)) ? SECFRAMEW : NORMFRAMEW); int y_adj; for(y_adj = 0; (ttyclock->geo.y - y_adj) > (COLS - new_w - 1); ++y_adj); clock_move(ttyclock->geo.x, (ttyclock->geo.y - y_adj), new_w, ttyclock->geo.h); set_center(ttyclock->option.center); return; } void set_center(Bool b) { if((ttyclock->option.center = b)) { ttyclock->option.rebound = False; clock_move((LINES / 2 - (ttyclock->geo.h / 2)), (COLS / 2 - (ttyclock->geo.w / 2)), ttyclock->geo.w, ttyclock->geo.h); } return; } void set_box(Bool b) { ttyclock->option.box = b; wbkgdset(ttyclock->framewin, COLOR_PAIR(0)); wbkgdset(ttyclock->datewin, COLOR_PAIR(0)); if(ttyclock->option.box) { wbkgdset(ttyclock->framewin, COLOR_PAIR(0)); wbkgdset(ttyclock->datewin, COLOR_PAIR(0)); box(ttyclock->framewin, 0, 0); box(ttyclock->datewin, 0, 0); } else { wborder(ttyclock->framewin, ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '); wborder(ttyclock->datewin, ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '); } wrefresh(ttyclock->datewin); wrefresh(ttyclock->framewin); } void key_event(void) { int i, c; struct timespec length = { ttyclock->option.delay, ttyclock->option.nsdelay }; if (ttyclock->option.screensaver) { c = wgetch(stdscr); if(c != ERR && ttyclock->option.noquit == False) { ttyclock->running = False; } else { nanosleep(&length, NULL); for(i = 0; i < 8; ++i) if(c == (i + '0')) { ttyclock->option.color = i; init_pair(1, ttyclock->bg, i); init_pair(2, i, ttyclock->bg); } } return; } switch(c = wgetch(stdscr)) { case KEY_UP: case 'k': case 'K': if(ttyclock->geo.x >= 1 && !ttyclock->option.center) clock_move(ttyclock->geo.x - 1, ttyclock->geo.y, ttyclock->geo.w, ttyclock->geo.h); break; case KEY_DOWN: case 'j': case 'J': if(ttyclock->geo.x <= (LINES - ttyclock->geo.h - DATEWINH) && !ttyclock->option.center) clock_move(ttyclock->geo.x + 1, ttyclock->geo.y, ttyclock->geo.w, ttyclock->geo.h); break; case KEY_LEFT: case 'h': case 'H': if(ttyclock->geo.y >= 1 && !ttyclock->option.center) clock_move(ttyclock->geo.x, ttyclock->geo.y - 1, ttyclock->geo.w, ttyclock->geo.h); break; case KEY_RIGHT: case 'l': case 'L': if(ttyclock->geo.y <= (COLS - ttyclock->geo.w - 1) && !ttyclock->option.center) clock_move(ttyclock->geo.x, ttyclock->geo.y + 1, ttyclock->geo.w, ttyclock->geo.h); break; case 'q': case 'Q': if (ttyclock->option.noquit == False) ttyclock->running = False; break; case 's': case 'S': set_second(); break; case 't': case 'T': ttyclock->option.twelve = !ttyclock->option.twelve; /* Set the new ttyclock->date.datestr to resize date window */ update_hour(); clock_move(ttyclock->geo.x, ttyclock->geo.y, ttyclock->geo.w, ttyclock->geo.h); break; case 'c': case 'C': set_center(!ttyclock->option.center); break; case 'b': case 'B': ttyclock->option.bold = !ttyclock->option.bold; break; case 'r': case 'R': ttyclock->option.rebound = !ttyclock->option.rebound; if(ttyclock->option.rebound && ttyclock->option.center) ttyclock->option.center = False; break; case 'x': case 'X': set_box(!ttyclock->option.box); break; default: nanosleep(&length, NULL); for(i = 0; i < 8; ++i) if(c == (i + '0')) { ttyclock->option.color = i; init_pair(1, ttyclock->bg, i); init_pair(2, i, ttyclock->bg); } break; } return; } int main(int argc, char **argv) { int c; /* Alloc ttyclock */ ttyclock = malloc(sizeof(ttyclock_t)); assert(ttyclock != NULL); memset(ttyclock, 0, sizeof(ttyclock_t)); ttyclock->option.date = True; /* Date format */ ttyclock->option.format = malloc(sizeof(char) * 100); /* Default date format */ strncpy(ttyclock->option.format, "%F", 100); /* Default color */ ttyclock->option.color = COLOR_GREEN; /* COLOR_GREEN = 2 */ /* Default delay */ ttyclock->option.delay = 1; /* 1FPS */ ttyclock->option.nsdelay = 0; /* -0FPS */ ttyclock->option.blink = False; atexit(cleanup); while ((c = getopt(argc, argv, "iuvsScbtrhBxnDC:f:d:T:a:")) != -1) { switch(c) { case 'h': default: printf("usage : tty-clock [-iuvsScbtrahDBxn] [-C [0-7]] [-f format] [-d delay] [-a nsdelay] [-T tty] \n" " -s Show seconds \n" " -S Screensaver mode \n" " -x Show box \n" " -c Set the clock at the center of the terminal \n" " -C [0-7] Set the clock color \n" " -b Use bold colors \n" " -t Set the hour in 12h format \n" " -u Use UTC time \n" " -T tty Display the clock on the specified terminal \n" " -r Do rebound the clock \n" " -f format Set the date format \n" " -n Don't quit on keypress \n" " -v Show tty-clock version \n" " -i Show some info about tty-clock \n" " -h Show this page \n" " -D Hide date \n" " -B Enable blinking colon \n" " -d delay Set the delay between two redraws of the clock. Default 1s. \n" " -a nsdelay Additional delay between two redraws in nanoseconds. Default 0ns.\n"); exit(EXIT_SUCCESS); break; case 'i': puts("TTY-Clock 2 © by Martin Duquesnoy (xorg62@gmail.com), Grey (grey@greytheory.net)"); exit(EXIT_SUCCESS); break; case 'u': ttyclock->option.utc = True; break; case 'v': puts("TTY-Clock 2 © devel version"); exit(EXIT_SUCCESS); break; case 's': ttyclock->option.second = True; break; case 'S': ttyclock->option.screensaver = True; break; case 'c': ttyclock->option.center = True; break; case 'b': ttyclock->option.bold = True; break; case 'C': if(atoi(optarg) >= 0 && atoi(optarg) < 8) ttyclock->option.color = atoi(optarg); break; case 't': ttyclock->option.twelve = True; break; case 'r': ttyclock->option.rebound = True; break; case 'f': strncpy(ttyclock->option.format, optarg, 100); break; case 'd': if(atol(optarg) >= 0 && atol(optarg) < 100) ttyclock->option.delay = atol(optarg); break; case 'D': ttyclock->option.date = False; break; case 'B': ttyclock->option.blink = True; break; case 'a': if(atol(optarg) >= 0 && atol(optarg) < 1000000000) ttyclock->option.nsdelay = atol(optarg); break; case 'x': ttyclock->option.box = True; break; case 'T': { struct stat sbuf; if (stat(optarg, &sbuf) == -1) { fprintf(stderr, "tty-clock: error: couldn't stat '%s': %s.\n", optarg, strerror(errno)); exit(EXIT_FAILURE); } else if (!S_ISCHR(sbuf.st_mode)) { fprintf(stderr, "tty-clock: error: '%s' doesn't appear to be a character device.\n", optarg); exit(EXIT_FAILURE); } else { if (ttyclock->tty) free(ttyclock->tty); ttyclock->tty = strdup(optarg); }} break; case 'n': ttyclock->option.noquit = True; break; } } init(); attron(A_BLINK); while(ttyclock->running) { clock_rebound(); update_hour(); draw_clock(); key_event(); } endwin(); return 0; } // vim: expandtab tabstop=4 softtabstop=4 shiftwidth=4