/*
     KEYTEST : Mac OS X Key Test Program

     usage is: keytest [-n] <R>

     Optional -n switch puts Keytest in "no timeout" mode

     no other options currently supported.

  +--------------------------------------------------------------------+
  | This program accepts keystrokes and prints their hexadecimal value |
  | on the screen. The special character '$' terminates the program,   |
  | just like its Linux, DOS, QUNIX and XMS counterparts.              |
  +--------------------------------------------------------------------+

*/
#include <stdio.h>
#include "curses.h"
#include <signal.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/times.h>
#include <ctype.h>

/*
     Global  variables
*/
static char masthead1 [] = "MacOSX Keytest v1.1  --  Press '$' to Exit";
static char masthead2 [] = "=========================================";
static char masthead3 [] = "Go to www.campbellware.com for the latest Keytest";

static WINDOW *kt_win;             /* the ncurses window for Keytest  */
static char   out_buf[256];        /* Buffer for formatting output    */

static int row,col;                /* screen cursor position          */
static int curr,lo,hi;             /* directory list position, limits */
static int curses_flag = 0;        /* curses flag 0=off               */
static int notimeout_mode = 0;     /* timeout vs. no timeout mode     */
static int i, n;                   /* index variables                 */

static struct  tms time_data;      /* for current clock info          */
static clock_t curr_ticks;         /* current sytem clock ticks       */
static clock_t last_ticks;         /* clock ticks at last char recv'd */
static double  elapsed_ticks;      /* difference between last & curr  */

static MEVENT  mouse_event;        /* Mouse event information         */

/*
     forward references
*/

void init_curses(), end_curses(), process_keystrokes(), report_mouse_event(),
init_screen(), init_signals(), signals();


int main ( argc, argv, environ )
/*
------------------------------------------------------------------------------
Main line routine. Does the following:

     0) initialize: args, signals.
     1) setup curses.
     2) read key.
     3) display key.
     4) process exit ('$') command if present, else go to (2).
     5) terminate curses.
     6) return.
------------------------------------------------------------------------------
*/

int argc;
char *argv[], *environ[];

{
   int x;

   if ( argc > 1 ) 
   {
      if ( strcmp(argv[1], "-n") == 0 )
         notimeout_mode = 1;
      else
      {
         printf ( "Usage: keytest [-n]\n" );
         exit ( 0 );
      }
   }

   init_signals();               /* trap the signals */
   init_curses();                /* establish curses */

   process_keystrokes ();        /* mainline loop    */

   end_curses();
   exit(0);
}

void process_keystrokes ()
/*
------------------------------------------------------------------------------
     mainline loop:
          obtain keystroke;
          if '$' cmd, process same;
          else, echo back to screen;
          repeat;
------------------------------------------------------------------------------
*/

{

#define I_NULL       0
#define I_NTO        1
#define I_MOUSE      2
#define I_CHAR       3

   int  c;                       /* character input           */
   int  char_count = 0;          /* number of received chars  */
   int  line_count = 0;          /* number of lines printed   */
   int  last_input = I_NULL;     /* Input type                */

   while (1) {

     wrefresh ( kt_win );
     c = getch();

     /* Check for being told to quit - do so if told */

     if ( c == '$' )
        return;

     /* Every nine chars, output a CR */

     if ( char_count >= 9 ) {
        wprintw ( kt_win, "\n\r" );
        line_count++;
        char_count = 0;
     }

     /* Every 10 lines, clear the screen */

     if ( line_count >= 10 )
     {
        wmove ( kt_win, 8, 0 );
        wclrtobot ( kt_win );
        char_count = 0;
        line_count = 0;
     }

     /* Mouse events - report the value of KEY_MOUSE, and name the event */

     if ( c == KEY_MOUSE )
     {
        /* We got a mouse event - get the associated data and report the event */

        getmouse ( &mouse_event );

        if ( last_input == I_CHAR )
           wprintw ( kt_win, "\n" );

        report_mouse_event (c);

        line_count += 1;
        last_input = I_MOUSE;

     } /* KEY_MOUSE */

     /* No timeout mode - report the key and the elapsed time since last key */

     else if ( notimeout_mode )
     {
        curr_ticks    = times ( &time_data );
        elapsed_ticks = (double) (curr_ticks - last_ticks);
        last_ticks    = curr_ticks;

        wprintw ( kt_win, "%s %3x %s %f\n", "Key = ", c, "Elapsed Ticks = ", elapsed_ticks );

        line_count += 1;
        last_input = I_NTO;
     }

     /* Ordinary characters - just echo their hex code to the screen */

     else
     { 
        wprintw ( kt_win, "%3x ", c );

        char_count += 1;
        last_input = I_CHAR;
     }

  }
}

void report_mouse_event ( c )
/*
------------------------------------------------------------------------------
Report_mouse_event. Parses the mouse_event structure and prints out a humam
readable indication of what the mouse event was.
------------------------------------------------------------------------------
*/
int c;

{
   wprintw ( kt_win, "%3x %s %d, %d: ", c, "Mouse event at", 
             mouse_event.x, mouse_event.y );

   /* Button 1 events */

   if ( mouse_event.bstate & BUTTON1_CLICKED )
      wprintw ( kt_win, "%s\n", "Button 1 clicked" );
   else if ( mouse_event.bstate & BUTTON1_PRESSED )
      wprintw ( kt_win, "%s\n", "Button 1 pressed" );
   else if ( mouse_event.bstate & BUTTON1_RELEASED )
      wprintw ( kt_win, "%s\n", "Button 1 released" );
   else if ( mouse_event.bstate & BUTTON1_DOUBLE_CLICKED )
      wprintw ( kt_win, "%s\n", "Button 1 double clicked" );
   else if ( mouse_event.bstate & BUTTON1_TRIPLE_CLICKED )
      wprintw ( kt_win, "%s\n", "Button 1 triple clicked" );

   /* Button 2 events */

   else if ( mouse_event.bstate & BUTTON2_CLICKED )
      wprintw ( kt_win, "%s\n", "Button 2 clicked" );
   else if ( mouse_event.bstate & BUTTON2_PRESSED )
      wprintw ( kt_win, "%s\n", "Button 2 pressed" );
   else if ( mouse_event.bstate & BUTTON2_RELEASED )
      wprintw ( kt_win, "%s\n", "Button 2 released" );
   else if ( mouse_event.bstate & BUTTON2_DOUBLE_CLICKED )
      wprintw ( kt_win, "%s\n", "Button 2 double clicked" );
   else if ( mouse_event.bstate & BUTTON2_TRIPLE_CLICKED )
      wprintw ( kt_win, "%s\n", "Button 2 triple clicked" );

   /* Button 3 events */

   else if ( mouse_event.bstate & BUTTON3_CLICKED )
      wprintw ( kt_win, "%s\n", "Button 3 clicked" );
   else if ( mouse_event.bstate & BUTTON3_PRESSED )
      wprintw ( kt_win, "%s\n", "Button 3 pressed" );
   else if ( mouse_event.bstate & BUTTON3_RELEASED )
      wprintw ( kt_win, "%s\n", "Button 3 released" );
   else if ( mouse_event.bstate & BUTTON3_DOUBLE_CLICKED )
      wprintw ( kt_win, "%s\n", "Button 3 double clicked" );
   else if ( mouse_event.bstate & BUTTON3_TRIPLE_CLICKED )
      wprintw ( kt_win, "%s\n", "Button 3 triple clicked" );

   /* Unsupported events */

   else
      wprintw ( kt_win, "%s\n", "Unsupported mouse event" );

} /* report_mouse_event */

void init_curses ()
/*
------------------------------------------------------------------------------
   This routine establishes the  curses environment
   It then puts out a masthead message and moves the cursor to the starting 
   position
------------------------------------------------------------------------------
*/

{
     /* Set up ncurses */

     kt_win = initscr();
     keypad ( kt_win, TRUE); 
     raw();
     noecho();

     /* If needed by user option, baseline the current time */

     if ( notimeout_mode )
     {
        notimeout ( kt_win, TRUE );
        keypad ( kt_win, FALSE ); 
        curr_ticks = last_ticks = times ( &time_data );
     }

     /* Enable receipt of all mouse events */

     mousemask ( ALL_MOUSE_EVENTS, NULL );

     /* Output the masthead and set the cursor to its initial position */

     mvwaddstr ( kt_win, 1, 0, masthead1 );
     mvwaddstr ( kt_win, 2, 0, masthead2 );
     mvwaddstr ( kt_win, 4, 0, masthead3 );

     sprintf ( out_buf, "%s %d, %d", "ncurses reports that LINES, COLS = ", LINES, COLS );
     mvwaddstr ( kt_win, 6, 0, out_buf );

     wmove ( kt_win, 8, 0 );
}

void end_curses ()
/*
------------------------------------------------------------------------------
   This routine removes the curses environment
------------------------------------------------------------------------------
*/

{
     mvwaddstr ( kt_win, LINES-2, 0, "Keytest terminated\n");
     wrefresh ( kt_win );

     endwin();
}

void init_screen ()
/*
------------------------------------------------------------------------------
   Routine to initialize the screen to nominal state
------------------------------------------------------------------------------
*/
{

}


void init_signals ()
/*
------------------------------------------------------------------------------
   Grab all signals
------------------------------------------------------------------------------
*/

{
     signal (SIGQUIT, signals);
     signal (SIGHUP,  signals);       /*    trap    */
     signal (SIGSEGV, signals);       /* interrupts */
     signal (SIGINT,  signals);
     signal (SIGBUS,  signals);
     signal (SIGTERM, signals);
}


void signals ( sig )
/*
------------------------------------------------------------------------------
   Control comes here on any signal.
------------------------------------------------------------------------------
*/

int sig;

{
   end_curses();
   exit(2);
}

