text: /*
   *      TTY-CLOCK Main file.
   *      Copyright © 2009-2018 tty-clock contributors
   *      Copyright © 2008 Martin Duquesnoy <xorg62@gmail.com>
   *      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-tomato.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 reset_count(void)
  {
      ttyclock->beginning_time = time(NULL); 
      ttyclock->count++;
      int c = ttyclock->count;
      if (c % 2 == 1) ttyclock->phase = WORK;
      else if (c % 8 == 0) ttyclock->phase = PAUSE;
      else ttyclock->phase = LONG_PAUSE;
  
      if(ttyclock->phase == WORK)
          ttyclock->option.color = 1;
      else
          ttyclock->option.color = 2;
  }
  
  void update_hour(void)
  {
      ttyclock->lt = time(NULL);
      // ttyclock->tm = localtime(&(ttyclock->lt));
      // if(ttyclock->option.utc) {
      //     ttyclock->tm = gmtime(&(ttyclock->lt));
      // }
  
      // int ses[3];
      // for(int i = 0; i < 3; i++) ses[i] = sessions[ttyclock->phase][i];
      // int target = ses[2] + 60 * (ses[1] + 60 * ses[0]);
      int target = 60*25;
  
      int diff = (int)(difftime(ttyclock->lt, ttyclock->beginning_time));
      diff = target - diff;
      if (diff == 0){
          reset_count();
          update_hour();
          return;
      }
      int secs = diff % 60;
      diff /= 60;
      int mins = diff % 60;
      diff /= 60;
  
      ttyclock->date.second[1] = secs % 10;
      ttyclock->date.second[0] = secs / 10;
      ttyclock->date.hour[0] = diff % 10;
      ttyclock->date.hour[1] = (diff / 10) % 10;
      ttyclock->date.minute[0] = mins / 10;
      ttyclock->date.minute[1] = mins % 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)
  {       
      int display_hours = ttyclock->option.hours;
      int delta = 0;
      if(display_hours){
          /* Draw hour numbers */
          draw_number(ttyclock->date.hour[0], 1, 1);
          draw_number(ttyclock->date.hour[1], 1, 8);
          delta = 19;
      }
  
      chtype dotcolor = COLOR_PAIR(1);
      if (ttyclock->option.blink && time(NULL) % 2 == 0)
          dotcolor = COLOR_PAIR(2);
  
      if(display_hours){
          /* 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, 1 + delta);
      draw_number(ttyclock->date.minute[1], 1, 8 + delta);
  
      /* 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);
          // Originally the 3rd (starting from 1) was NORMFRAMEW
          mvwaddstr(ttyclock->framewin, 2, 16 + delta, "  ");
          mvwaddstr(ttyclock->framewin, 4, 16 + delta, "  ");
  
          /* Draw second numbers */
          draw_number(ttyclock->date.second[0], 1, 20 + delta);
          draw_number(ttyclock->date.second[1], 1, 27 + delta);
      }
  
      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;
      ttyclock->option.second = True;
      ttyclock->option.hours = False;
  
      /* 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;
      ttyclock->count = 0;
      reset_count();
  
      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 hours                                     \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.hours = 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) {
                                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
  