Thread: Display timer same time as input

  1. #1
    Registered User
    Join Date
    Sep 2003
    Posts
    224

    Display timer same time as input

    Hi all,

    I want to display a timer the same time I am accepting input in a
    curses(3) program. I am using alarm(3) as my timer. The problem is that
    the timer is not updated when I am accepting input; it is updated after I
    press a key. Here is the code:

    Code:
    #include <ctype.h>
    #include <curses.h>
    #include <unistd.h>
    #include <signal.h>
    #include <stdlib.h>
    
    static char _hour = 0, _min = 0, _sec = 0;
    
    void disp_timer()
    {
      char sprintf_buf[10];
    
      sprintf(sprintf_buf, "%02i:%02i:%02i", _hour, _min, _sec);
      mvaddstr(3, 3, sprintf_buf);
    }
    
    void alarm_handler(int sig)
    {
      _sec++;
         
      if(_sec == 60)
      {
        _sec = 0;
        _min++;
      
        if(_min == 60)
        {
          _min = 0;
         _hour++;
        }   
      }
    
      disp_timer();
      signal(SIGALRM, alarm_handler); /* watch on alarm again */
      alarm(1);
    }
    
    void finish(int sig)
    {
      endwin();
      exit(0);
    }
    
    int main()
    {
      int i;
      char again, ch;
      char sprintf_buf[15];
    
      initscr();
      noecho();
      cbreak();
      disp_timer();
      signal(SIGINT, finish); /* ^C exits and cleans up program */
      signal(SIGALRM, alarm_handler);
      alarm(1);
    
      again = 1;
    
      do
      {
        for(i = 0; i < 10; i++)
        {
          clear();
          disp_timer();
          sprintf(sprintf_buf, "Question %i: ", i);
          mvaddstr(0, 0, sprintf_buf);
          getch();
          sprintf(sprintf_buf, "Answer: %i", i);
          mvaddstr(1, 0, sprintf_buf);
          getch();
        }
    
        clear();
        disp_timer();
        mvaddstr(0, 0, "Again (y/n)? ");
        ch = getch();
    
        if(toupper(ch) == 'N')
          again = 0;
      } while(again);
    
      finish(0);
    
      return 0;
    }
    Any ideas?

    Thanks,
    Yasir

  2. #2
    Registered User
    Join Date
    Sep 2003
    Posts
    224
    Anyone?

    I played around with this last night. I forked a child process that would only display the timer. Although the timer was being incremented, nothing was being displayed on the screen. Then I got the idea of using another window to display the timer. I still can't get it to work, but I think I'm close.

    Code:
    #include <ctype.h>
    #include <curses.h>
    #include <unistd.h>
    #include <signal.h>
    #include <stdlib.h>
    #include <sys/wait.h>
    
    char _hour = 0, _min = 0, _sec = 0;
    WINDOW *timer_win;
    
    void disp_timer()
    {
      char sprintf_buf[10];
    
      sprintf(sprintf_buf, "%02i:%02i:%02i", _hour, _min, _sec);
      mvwaddstr(timer_win, 3, 3, sprintf_buf);
    }
    
    void alarm_handler(int sig)
    {
      _sec++;
         
      if(_sec == 60)
      {
        _sec = 0;
        _min++;
      
        if(_min == 60)
        {
          _min = 0;
         _hour++;
        }   
      }
    
      disp_timer();
      signal(SIGALRM, alarm_handler); /* watch on alarm again */
      alarm(1);
    }
    
    void finish(int sig)
    {
      endwin();
      exit(0);
    }
    
    int main()
    {
      int i;
      char again, ch;
      char sprintf_buf[15];
    
      initscr();
      noecho();
      cbreak();
    
      timer_win = newwin(1, 10, 3, 3);
    
      disp_timer();
      signal(SIGINT, finish); /* ^C exits and cleans up program */
      signal(SIGALRM, alarm_handler);
      alarm(1);
    
      again = 1;
    
      do
      {
        for(i = 0; i < 10; i++)
        {
          clear(); /* should clear only stdscr, not timer_win */
          sprintf(sprintf_buf, "Question %i: ", i);
          mvaddstr(0, 0, sprintf_buf);
          getch();
          sprintf(sprintf_buf, "Answer: %i", i);
          mvaddstr(1, 0, sprintf_buf);
          getch();
        }
    
        clear();
        mvaddstr(0, 0, "Again (y/n)? ");
        ch = getch();
    
        if(toupper(ch) == 'N')
          again = 0;
      } while(again);
    
      finish(0);
    
      return 0;
    }

  3. #3
    Sr. Software Engineer filker0's Avatar
    Join Date
    Sep 2005
    Location
    West Virginia
    Posts
    235
    mvaddstr(3X) does not update the display -- you must call refresh(3X) or wrefresh(3X) to update the actual display in your alarm handler.

    I hope this helps.

    [edit]A subprocess has the problem that it's probably not dealing with the same curses screen buffer, so it will wipe out anything that's on the display. A separate thread would work, assuming that the curses library is thread safe (I don't know off the top of my head.)[/edit]
    Last edited by filker0; 12-28-2005 at 09:52 AM. Reason: Update to reflect second posting
    Insert obnoxious but pithy remark here

  4. #4
    Registered User
    Join Date
    Sep 2003
    Posts
    224
    Ok, I'm getting closer. However, here's the problem: when I call getch(), it refreshes the main window, thus placing the main window on top of timer_win. If the alarm goes off, timer_win will be displayed, but since the alarm is happening asynchronously of getch(), the timer disappears until the alarm goes off again. Also, the cursor jumps around. Is there a way to get around this? By the way, when I had created two process, the main window displayed everything correctly, but timer_win was never displayed. I did the fork after the call to initscr(). I even tried doing dupwin().
    Code:
    #include <ctype.h>
    #include <curses.h>
    #include <unistd.h>
    #include <signal.h>
    #include <stdlib.h>
    #include <sys/wait.h>
    
    char _hour = 0, _min = 0, _sec = 0;
    WINDOW *timer_win;
    
    void disp_timer()
    {
      mvwprintw(timer_win, 0, 0, "%02i:%02i:%02i", _hour, _min, _sec);
      wrefresh(timer_win);
    }
    
    void alarm_handler(int sig)
    {
      _sec++;
    
      if(_sec == 60)
      {
        _sec = 0;
        _min++;
    
        if(_min == 60)
        {
          _min = 0;
         _hour++;
        }
      }
    
      disp_timer();
      signal(SIGALRM, alarm_handler); /* watch on alarm again */
      alarm(1);
    }
    
    void finish(int sig)
    {
      endwin();
      exit(0);
    }
    
    int main()
    {
      int i;
      char again, ch;
    
      initscr();
      noecho();
      cbreak();
    
      timer_win = newwin(1, 10, 3, 3);
    
      disp_timer();
      signal(SIGINT, finish); /* ^C exits and cleans up program */
      signal(SIGALRM, alarm_handler);
      alarm(1);
    
      again = 1;
    
      do
      {
        for(i = 0; i < 10; i++)
        {
          clear(); /* should clear only stdscr, not timer_win */
          mvprintw(0, 0, "Question %i: ", i);
          disp_timer();
          getch();
          mvprintw(1, 0, "Answer: %i", i);
          disp_timer();
          getch();
        }
    
        clear();
        mvaddstr(0, 0, "Again (y/n)? ");
        disp_timer();
        ch = getch();
    
        if(toupper(ch) == 'N')
          again = 0;
      } while(again);
    
      finish(0);
    
      return 0;
    }

  5. #5
    Registered User
    Join Date
    Sep 2003
    Posts
    224
    I think I'll be able to handle the cursor positioning with getyx() and move(), but the overlapping window problem still persists.

  6. #6
    Registered User
    Join Date
    Sep 2003
    Posts
    224
    I had this wild revelation last night to use two windows, because that way the windows won't occlude one another. You don't need to use stdscr. I also found out about setitimer()
    Code:
    #include <sys/time.h>
    
    #include <ctype.h>
    #include <curses.h>
    #include <signal.h>
    #include <stdlib.h>
    #include <strings.h>
    
    int ques_x, ques_y;
    char _hour = 0, _min = 0, _sec = 0;
    WINDOW *timer_win, *ques_win;
    
    void disp_timer()
    {
      mvwprintw(timer_win, 0, 0, "%02i:%02i:%02i", _hour, _min, _sec);
      wrefresh(timer_win);
    
      /* move the cursor to where it was in ques_win */
      wmove(ques_win, ques_y, ques_x);
      wrefresh(ques_win);
    }
    
    void alarm_handler(int sig)
    {
      _sec++;
         
      if(_sec == 60)
      {
        _sec = 0;
        _min++;
      
        if(_min == 60)
        {
          _min = 0;
         _hour++;
        }   
      }
    
      disp_timer();
    }
    
    void finish(int sig)
    {
      endwin();
      exit(0);
    }
    
    int main()
    {
      int i;
      char again, ch;
      struct itimerval value;
    
      initscr();
      noecho();
      cbreak();
    
      timer_win = newwin(2, 10, 3, 0);
      ques_win = newwin(2, COLS, 0, 0);
    
      disp_timer();
      signal(SIGINT, finish); /* ^C exits and cleans up program */
      signal(SIGALRM, alarm_handler);
      bzero(&value, sizeof(value));
    
      value.it_interval.tv_sec = 1;
      value.it_value.tv_sec = 1;
    
      setitimer(ITIMER_REAL, &value, NULL); /* alarm reset after timer countdown */
    
      again = 1;
    
      do
      {
        for(i = 0; i < 10; i++)
        {
          wclear(ques_win);
          mvwprintw(ques_win, 0, 0, "Question %i: ", i);
          getyx(ques_win, ques_y, ques_x); /* save the current cursor position */
          wgetch(ques_win);
          mvwprintw(ques_win, 1, 0, "Answer: %i", i);
          getyx(ques_win, ques_y, ques_x);
          wgetch(ques_win);
        }
    
        wclear(ques_win);
        mvwaddstr(ques_win, 0, 0, "Again (y/n)? ");
        getyx(ques_win, ques_y, ques_x);      
    
        ch = wgetch(ques_win);
    
        if(toupper(ch) == 'N')
          again = 0;
      } while(again);
    
      finish(0);
    
      return 0;
    }

  7. #7
    Registered User
    Join Date
    Sep 2003
    Posts
    224
    I also found this great tutorial:
    http://snap.nlc.dcccd.edu/learn/full...ap6/chap6.html

  8. #8
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    What an ugly tutorial - wtf use is a lot of demo code if it's all rendered using images which you can't copy/paste into your own code?
    I'd search for the original tutorial that seems to be ripped from rather than that.

    As already mentioned, you have concurrency issues as well. Signal handlers are restricted environments in which only a subset of available functions are guaranteed to work properly.

  9. #9
    Registered User
    Join Date
    Sep 2003
    Posts
    224
    I don't understand what concurrency issues I could have in the code I posted.
    I found not having the ability to copy and paste annoying, too, but the content was good. It had many examples. The professor also has many other tutorials.

  10. #10
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    > I don't understand what concurrency issues I could have in the code I posted.
    Like calling mvwprintw() from within the main code and the signal handler.

  11. #11
    Registered User
    Join Date
    Sep 2003
    Posts
    224
    Why would calling mvwprintw() within the signal handler cause concurrency issues? I have no choice: I want to be able to display the timer while accepting input from the user.

  12. #12
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    > Why would calling mvwprintw() within the signal handler cause concurrency issues?
    http://www.dwheeler.com/secure-progr...O/signals.html

    > I want to be able to display the timer while accepting input from the user.
    There's always another way.

    Code:
    #include <sys/time.h>
    #include <ctype.h>
    #include <curses.h>
    #include <signal.h>
    #include <stdlib.h>
    #include <string.h> // not strings.h
    #include <errno.h>
    
    int     hour = 0, min = 0, sec = 0;
    
    void disp_timer ( WINDOW *ques_win )
    {
      mvwprintw(ques_win, 2, 0, "%02i:%02i:%02i", hour, min, sec);
      wrefresh(ques_win);
    }
    
    /* just update some simple variables in the signal handler */
    void alarm_handler ( int sig )
    {
      sec++;
      if(sec == 60)
      {
        sec = 0;
        min++;
        if(min == 60)
        {
          min = 0;
          hour++;
        }
      }
    }
    
    void finish(int sig)
    {
      endwin();
      exit(0);
    }
    
    int main()
    {
      WINDOW *ques_win;
      int     i;
      int     again, ch = 0;
      struct itimerval value;
    
      initscr();
      noecho();
      cbreak();
      halfdelay(10);
    
      ques_win = newwin(3, COLS, 0, 0);
    
      signal(SIGALRM, alarm_handler);
      memset(&value, 0, sizeof(value));
      value.it_interval.tv_sec = 1;
      value.it_value.tv_sec    = 1;
      setitimer(ITIMER_REAL, &value, NULL); /* alarm reset after timer countdown */
    
      signal(SIGINT, finish); /* ^C exits and cleans up program */
    
      again = 1;
      do
      {
        for(i = 0; i < 10;i++)
        {
          wclear(ques_win);
          disp_timer(ques_win);
          mvwprintw(ques_win, 0, 0, "Question %i: ", i);
          mvwprintw(ques_win, 1, 0, "Answer: %i", i);
          wrefresh(ques_win);
          while ( (ch = wgetch(ques_win)) == ERR ) {
            int ques_x, ques_y;
            /* timed out a second */
            /* save/restore cursor and update time */
            getyx(ques_win, ques_y, ques_x);
            disp_timer(ques_win);
            wmove(ques_win, ques_y, ques_x);
            wrefresh(ques_win);
          }
        }
    
        wclear(ques_win);
        mvwaddstr(ques_win, 0, 0, "Again (y/n)? ");
    
        ch = wgetch(ques_win);
    
        if(toupper(ch) == 'N')
          again = 0;
      } while(again);
    
      finish(0);
      return 0;
    }
    In fact, you could probably drop the timer signal altogether and just update the time at every key timeout.

  13. #13
    Registered User
    Join Date
    Sep 2003
    Posts
    224
    The timer in your program is only updated when you press a key; it does not run at the same time as accepting input.
    Also, what's wrong with bzero()?

  14. #14
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    > The timer in your program is only updated when you press a key
    Works fine here - did you remember the call to halfdelay() to cause wgetch() to return after a timeout period?

    > Also, what's wrong with bzero()?
    memset is standard, bzero isn't.

    My man page for bzero states
    This function is deprecated -- use memset in new programs.

  15. #15
    Registered User
    Join Date
    Sep 2003
    Posts
    224
    Yes, I just copied and pasted your code. I'm on NetBSD 2.1.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Need some help with C program writing
    By The_PC_Gamer in forum C Programming
    Replies: 9
    Last Post: 02-12-2008, 09:12 PM
  2. Need help with time
    By Gong in forum C++ Programming
    Replies: 7
    Last Post: 01-11-2007, 02:43 PM
  3. need help in time zone
    By Gong in forum C++ Programming
    Replies: 2
    Last Post: 01-03-2007, 04:44 AM
  4. About aes
    By gumit in forum C Programming
    Replies: 13
    Last Post: 10-24-2006, 03:42 PM
  5. Tab Controls - API
    By -KEN- in forum Windows Programming
    Replies: 7
    Last Post: 06-02-2002, 09:44 AM