
// SPDX-License-Identifier: CC-BY-NC-SA-4.0
//
// Copyright (C) 2026 Bit by Bit Signal Processing LLC (https://bxbsp.com)
//
// This work is placed under the "Creative Commons Attribution
// NonCommercial ShareAlike 4.0 International" license, known
// by the shortened acronym "CC-BY-NC-SA-4.0".
//
// This work is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// A CC-BY-NC-SA-4.0 license allows you to use this work for
// noncommercial purposes so long as attribution is made to the
// original author.  Modified versions of this work may be distributed,
// but only under the same license.  For further details, see the
// Creative Commons License "CC-BY-NC-SA-4.0".
//
// You should have received a copy of the CC-BY-NC-SA-4.0 license
// along with this work. If not, see
// <https://creativecommons.org/licenses/by-nc-sa/4.0/>.
//

#include <string.h>

#include "input_event_handler.hh"
//#include "displays.hh"
#include <cerrno>


#define memclear(s)  memset(&s, 0, sizeof(s))

const char *input_event_listener::ev_type[EV_CNT]       = { 0 };
const char *input_event_listener::ev_code_syn[SYN_CNT]  = { 0 };
const char *input_event_listener::ev_code_abs[ABS_CNT]  = { 0 };
const char *input_event_listener::ev_code_rel[REL_CNT]  = { 0 };
const char *input_event_listener::ev_code_key[KEY_CNT]  = { 0 };
const char *input_event_listener::ev_code_msc[MSC_CNT]  = { 0 };

char keycode_to_ascii(int code);


int range_limit(int x, int min, int max)
{
  if(x<min)
    x=min;
  if(x>=max)
    x=max-1;

  return x;
}



void input_event_listener::print_event(struct input_event& ie)
{
  char type[100];
  char code[100];
  static double last_time_ms = 0;
  static int32_t last_timestamp_time_us = 0;
  
  if(ev_type[ie.type])
    sprintf(type, "type:%s", ev_type[ie.type]);
  else
    sprintf(type, "unknown type:%d (0x%02X)", ie.type, ie.type);

  if(ie.type==EV_ABS && ev_code_abs[ie.code])
    sprintf(code, "code:%s", ev_code_abs[ie.code]);
  else if(ie.type==EV_SYN && ev_code_syn[ie.code])
    sprintf(code, "code:%s", ev_code_syn[ie.code]);
  else if(ie.type==EV_REL && ev_code_rel[ie.code])
    sprintf(code, "code:%s", ev_code_rel[ie.code]);
  else if(ie.type==EV_KEY && ev_code_key[ie.code])
    sprintf(code, "code:%s", ev_code_key[ie.code]);
  else if(ie.type==EV_MSC && ev_code_msc[ie.code])
    sprintf(code, "code:%s", ev_code_msc[ie.code]);
  else
    sprintf(code, "unknown code:%d (0x%02X)", ie.code, ie.code);

  char temp[200];

  
  if(ie.type==EV_MSC && ie.code == MSC_TIMESTAMP)
    {
      sprintf(temp, " (timestamp delta time %4.1fms)", double(ie.value - last_timestamp_time_us)/1000.0);
      last_timestamp_time_us = ie.value;
    }
  else
    {
      temp[0] = 0;
    }
  
  double time_ms = ie.time.tv_sec * 1000.0 + ie.time.tv_usec / 1000.0;
  double time_delta_ms = (last_time_ms==0) ? 0.0 : time_ms - last_time_ms;
  
  printf("delta time:%4.1fms %25s %25s value:%d%s\n",
	 time_delta_ms, type,
	 code, ie.value, temp);

  last_time_ms = time_ms;
}



void input_event_listener::init()
{  
  verbose                     = false;
  touchscreen                 = false;
  mouse                       = false;

  num_touches                 = 0;
  
  old_motion_touch_state      = MOTION_STATE_NONE;
  motion_touch_state          = MOTION_STATE_NONE;
  motion_event_start_us       = 0;

  key_press_state             = KEY_STATE_NONE;
  key_press_event             = false;
  key_release_event           = false;
  key_event_start_us          = 0;

  for(int i=0; i<MAX_SLOTS; i++)
    {
      tracking_id_for_each_slot[i] = -1;
      touch_for_each_slot[i] = -1;
    }
  
  ev_type[EV_SYN]       = "EV_SYN";
  ev_type[EV_KEY]       = "EV_KEY";
  ev_type[EV_REL]       = "EV_REL";
  ev_type[EV_ABS]       = "EV_ABS";
  ev_type[EV_MSC]       = "EV_MSC";
  ev_type[EV_SW]        = "EV_SW";
  ev_type[EV_LED]       = "EV_LED";
  ev_type[EV_SND]       = "EV_SND";
  ev_type[EV_REP]       = "EV_REP";
  ev_type[EV_FF]        = "EV_FF";
  ev_type[EV_PWR]       = "EV_PWR";
  ev_type[EV_FF_STATUS] = "EV_FF_STATUS";
  ev_type[EV_MAX]       = "EV_MAX";
  

  ev_code_syn[SYN_REPORT]    = "SYN_REPORT";
  ev_code_syn[SYN_CONFIG]    = "SYN_CONFIG";
  ev_code_syn[SYN_MT_REPORT] = "SYN_MT_REPORT";
  ev_code_syn[SYN_DROPPED]   = "SYN_DROPPED";
  ev_code_syn[SYN_MAX]       = "SYN_MAX";
  
  ev_code_abs[ABS_X]     = "ABS_X";
  ev_code_abs[ABS_Y]     = "ABS_Y";
  ev_code_abs[ABS_Z]     = "ABS_Z";
  ev_code_abs[ABS_RX]    = "ABS_RX";
  ev_code_abs[ABS_RY]    = "ABS_RY";
  ev_code_abs[ABS_RZ]    = "ABS_RZ";

  ev_code_abs[ABS_MT_SLOT]         = "ABS_MT_SLOT";
  ev_code_abs[ABS_MT_POSITION_X]   = "ABS_MT_POSITION_X";
  ev_code_abs[ABS_MT_POSITION_Y]   = "ABS_MT_POSITION_Y";
  ev_code_abs[ABS_MT_TRACKING_ID]  = "ABS_MT_TRACKING_ID";

  
  ev_code_rel[REL_X]      = "REL_X";
  ev_code_rel[REL_Y]      = "REL_Y";
  ev_code_rel[REL_Z]      = "REL_Z";
  ev_code_rel[REL_RX]     = "REL_RX";
  ev_code_rel[REL_RY]     = "REL_RY";
  ev_code_rel[REL_RZ]     = "REL_RZ";
  ev_code_rel[REL_HWHEEL] = "REL_WHEEL";
  ev_code_rel[REL_DIAL]   = "REL_DIAL";
  ev_code_rel[REL_WHEEL]  = "REL_WHEEL";
  ev_code_rel[REL_MISC]   = "REL_MISC";
  ev_code_rel[REL_MAX]    = "REL_MAX";
  
  ev_code_key[BTN_TOUCH]   = "BTN_TOUCH";
  ev_code_key[BTN_LEFT]    = "BTN_LEFT";
  ev_code_key[BTN_RIGHT]   = "BTN_RIGHT";
  ev_code_key[BTN_MIDDLE]  = "BTN_MIDDLE";
  ev_code_key[BTN_SIDE]    = "BTN_SIDE";
  ev_code_key[BTN_EXTRA]   = "BTN_EXTRA";
  ev_code_key[BTN_FORWARD] = "BTN_FORWARD";
  ev_code_key[BTN_BACK]    = "BTN_BACK";
  ev_code_key[BTN_TASK]    = "BTN_TASK";
  ev_code_key[KEY_MAX]     = "KEY_MAX";

  ev_code_msc[MSC_SERIAL]    = "MSC_SERIAL";
  ev_code_msc[MSC_PULSELED]  = "MSC_PULSELED";
  ev_code_msc[MSC_GESTURE]   = "MSC_GESTURE";
  ev_code_msc[MSC_RAW]       = "MSC_RAW";
  ev_code_msc[MSC_SCAN]      = "MSC_SCAN";
  ev_code_msc[MSC_TIMESTAMP] = "MSC_TIMESTAMP";

  current_slot = 0;


  fd = open(filename, O_RDONLY | O_NDELAY);
  if(fd == -1)
    {
      printf("Error opening device \"%s\".\n", filename);
      return;
      //exit(20);
    }


  int error = ioctl(fd, EVIOCGRAB, 1);
  if(error==-1)
    {
      printf("Error grabbing input device \"%s\".\n", filename);
      close(fd);
      fd = -1;
      return;
      //exit(20);
    }

  
  const char * absval[6] = { "Value", "Min", "Max", "Fuzz", "Flat", "Resolution" };
  int absX[6];
  int absY[6];

  for(int i=0; i<6; i++)
    {
      absX[i] = 0;
      absY[i] = 0;
    }

  int err1 = ioctl(fd, EVIOCGABS(ABS_MT_POSITION_X), absX);
  int err2 = ioctl(fd, EVIOCGABS(ABS_MT_POSITION_Y), absY);

  if(err1==0 && err2==0 && absX[2] && absY[2])
    {
      printf("Touchscreen ABS_MT_POSITION_X Properties\n");
      for(int x=0; x<6; x++)
	{
	  if(x<3 || absX[x])
	    {
	      printf("  %s: %d\n", absval[x], absX[x]);
	    }
	}
      
      printf("Touchscreen ABS_MT_POSITION_Y Properties\n");
      for(int y=0; y<6; y++)
	{
	  if(y<3 || absX[y])
	    {
	      printf("  %s: %d\n", absval[y], absY[y]);
	    }
	}
      
      touchscreen_to_screen_x_offset        = absX[1];
      touchscreen_to_screen_x_denominator   = absX[2]-absX[1];
      
      touchscreen_to_screen_y_offset        = absY[1];
      touchscreen_to_screen_y_denominator   = absY[2]-absX[1];

      touchscreen = true;
    }
}

input_event_listener::input_event_listener(const char* devname, int screen_width, int screen_height, input_event_handler* ieh)
{
  this->ieh = ieh;

  filename = new char[strlen(devname)+1];
  strcpy(filename, devname);

  touchscreen_to_screen_x_numerator     = screen_width;
  touchscreen_to_screen_y_numerator     = screen_height;
  touchscreen_to_screen_x_denominator   = 32768;
  touchscreen_to_screen_y_denominator   = 32768;

  init();
  
  start();
}


input_event_listener::~input_event_listener()
{
  close(fd);
  // thread is automatically terminated by base class destructor
}


static long long get_time_in_us()
{
  struct timeval t;
  gettimeofday(&t, 0);
  long long t_us = t.tv_sec*1000000ll + t.tv_usec;

  return t_us;
}


static bool motion_exceeds_limit(my_event& me1, my_event& me2, int limit)
{
  for(int i=0; i<me1.num_touches; i++)
    {
      if(abs(me1.c[i].x-me2.c[i].x)>limit)
	return true;
      if(abs(me1.c[i].y-me2.c[i].y)>limit)
	return true;
    }
  
  return false;
}


const char* motion_touch_state_to_string(int motion_touch_state)
{
  return 
    motion_touch_state==MOTION_STATE_NONE     ? "MOTION_STATE_NONE"    :
    motion_touch_state==MOTION_STATE_INIT     ? "MOTION_STATE_INIT"    :
    motion_touch_state==MOTION_STATE_HOLDING  ? "MOTION_STATE_HOLDING" :
    motion_touch_state==MOTION_STATE_MOVING   ? "MOTION_STATE_MOVING"  :
    motion_touch_state==MOTION_STATE_ERROR    ? "MOTION_STATE_ERROR"   :
    /**/                                        "MOTION_STATE_UNKNOWN" ;
}

void input_event_listener::handle_touchscreen_motion(input_event& ie, long long t_us)
{
  bool reportable_state = touch_state_handler.handle_touch_event(ie);
	  
  if(!reportable_state)
    return;

  if(verbose)
    touch_state_handler.print_touch_state();

  my_event previous_me = me;
	  
  me.num_touches = 0;
  for(int slot=0; slot<touch_state_handler.TOUCH_STATE_MAX_SLOTS; slot++)
    {
      if(touch_state_handler.slot[slot].x_valid && touch_state_handler.slot[slot].y_valid)
	{
	  int touchscreen_x = touch_state_handler.slot[slot].x;
	  int touchscreen_y = touch_state_handler.slot[slot].y;
	  
	  me.c[me.num_touches].x = touchscreen_to_screen_x_numerator * ( touchscreen_x - touchscreen_to_screen_x_offset ) / touchscreen_to_screen_x_denominator;
	  me.c[me.num_touches].y = touchscreen_to_screen_y_numerator * ( touchscreen_y - touchscreen_to_screen_y_offset ) / touchscreen_to_screen_y_denominator;
	  
	  me.num_touches++;
	}
      me.time_in_us = t_us;	      
    }
  
  if(me.num_touches!=touch_state_handler.num_touches)
    {
      printf("ERROR:  Num touch mismatch.\n");
      fflush(stdout);
      sleep(1);  // Give time for other threads to flush I/O?
      exit(20);
    }
  
  switch(motion_touch_state)
    {
    case MOTION_STATE_NONE:
      if(touch_state_handler.num_touches)
	{
	  // Don't send event immediately.  Wait a little for additional fingers.
	  // Touch event is sent as a timed event to wait for the fingers.
	  //printf("got touch event, %d touches.\n", num_touches);
	  motion_touch_state = MOTION_STATE_INIT;
	  motion_event_start_us = t_us;
	  me_at_touch = me;
	}
      break;
    case MOTION_STATE_INIT:
      // In this state, we're waiting for additional fingers.
      if(touch_state_handler.num_touches==0)
	{
	  // We just got an almost instantaneous touch.  Send both a touch and a release.
	  me_at_touch.type = EVENT_TOUCH;
	  send_event(me_at_touch);
	  me_at_touch.type = EVENT_RELEASE;
	  send_event(me_at_touch);
	  motion_touch_state = MOTION_STATE_NONE;
	}
      me_at_touch = me;  // Update the number of fingers and positons before the timeout for finger changes
      break;
    case MOTION_STATE_HOLDING:
      if(touch_state_handler.num_touches != me_at_touch.num_touches)
	{
	  // Any change in the number of fingers is the end of the gesture
	  previous_me.type = EVENT_RELEASE;
	  send_event(previous_me);
	  if(touch_state_handler.num_touches==0)
	    motion_touch_state = MOTION_STATE_NONE;
	  else
	    motion_touch_state = MOTION_STATE_ERROR;		    
	}
      else if(motion_exceeds_limit(me, me_at_touch, motion_to_break_hold))
	{	       
	  motion_touch_state = MOTION_STATE_MOVING;
	  me.type = EVENT_MOVE;
	  send_event(me);
	}
      break;
    case MOTION_STATE_MOVING:
      if(touch_state_handler.num_touches != me_at_touch.num_touches)
	{
	  // Any change in the number of fingers is the end of the gesture
	  previous_me.type = EVENT_RELEASE;
	  send_event(previous_me);
	  if(touch_state_handler.num_touches==0)
	    motion_touch_state = MOTION_STATE_NONE;
	  else
	    motion_touch_state = MOTION_STATE_ERROR;		    
	}
      else
	{
	  me.type = EVENT_MOVE;
	  send_event(me);
	}
      break;
      
    case MOTION_STATE_ERROR:
      if(touch_state_handler.num_touches==0)
	motion_touch_state = MOTION_STATE_NONE;
      break;
      
    default:
      printf("ERROR in input_event_handler state.  State is %d (%s).\n",
	     motion_touch_state, motion_touch_state_to_string(motion_touch_state));
      exit(20);
    }

  if(verbose)
    {
      if(motion_touch_state != old_motion_touch_state)
	{
	  printf("Device %s TOUCH motion_touch_state: %s -> %s\n",
		 filename,
		 motion_touch_state_to_string(old_motion_touch_state),
		 motion_touch_state_to_string(motion_touch_state));
	}
    }
}


void input_event_listener::handle_mouse_motion(input_event& ie, long long t_us)
{
  bool motion_touch_event        = false;
  bool motion_event              = false;
  bool wheel_event               = false;

  me.source_mouse = true;
  me.num_touches  = 1;
  
  if(ie.type==EV_REL)
    {
      motion_event    = true;
      
      if(ie.code == REL_X)
	me.c[0].x = range_limit(me.c[0].x+ie.value, 0, touchscreen_to_screen_x_numerator-1);
      else if(ie.code == REL_Y)
	me.c[0].y = range_limit(me.c[0].y+ie.value, 0, touchscreen_to_screen_y_numerator-1);
      
      // Don't do wheel events with the button pushed.  They're probably just a mistake.
      // Also, me.count is used for hold count in MOTION_STATE_HOLDING, so we don't
      // want to mess that up with a wheel event.
      if(ie.code == REL_WHEEL && motion_touch_state==MOTION_STATE_NONE)
	{
	  wheel_event = true;
	  me.count = ie.value;
	}
    }
  else if(ie.type==EV_KEY)
    {
      if(ie.code == BTN_LEFT || ie.code == BTN_MIDDLE || ie.code == BTN_RIGHT)
	{
	  if(ie.value == 0)
	    {
	      if(ie.code == BTN_LEFT)
		me.mouse_buttons_pressed &= ~MOUSE_BUTTON_LEFT;
	      else if(ie.code == BTN_RIGHT)
		me.mouse_buttons_pressed &= ~MOUSE_BUTTON_RIGHT;
	      else if(ie.code == BTN_MIDDLE)
		me.mouse_buttons_pressed &= ~MOUSE_BUTTON_MIDDLE;
	    }
	  else
	    {
	      motion_touch_event = true;		  
	      if(ie.code == BTN_LEFT)
		me.mouse_buttons_pressed |= MOUSE_BUTTON_LEFT;
	      else if(ie.code == BTN_RIGHT)
		me.mouse_buttons_pressed |= MOUSE_BUTTON_RIGHT;
	      else if(ie.code == BTN_MIDDLE)
		me.mouse_buttons_pressed |= MOUSE_BUTTON_MIDDLE;
	    }
	}
    }
	  
  switch(motion_touch_state)
    {
    case MOTION_STATE_NONE:
      if(wheel_event)
	{
	  wheel_event = false;
	  me.type = EVENT_WHEEL;
	  send_event(me);
	}
      else if(motion_touch_event)
	{
	  motion_touch_state = MOTION_STATE_INIT;  // Button press only, no movement, if within time
	  motion_event_start_us = t_us;
	  //me.num_touches = num_touches;
	  me_at_touch = me;
	}
      else if(motion_event)
	{  // Send mouse motion regardless of button presses, to display cursor
	  me.type = EVENT_POINTER_MOVE;
	  send_event(me);
	}
      
      break;
    case MOTION_STATE_INIT:
      if(!me.mouse_buttons_pressed) // Full release of all mouse buttons
	{
	  // We just got an almost instantaneous touch.  Send both a touch and a release.
	  motion_touch_state = MOTION_STATE_NONE;
	  me_at_touch.type = EVENT_TOUCH;
	  send_event(me_at_touch);
	  me_at_touch.type = EVENT_RELEASE;
	  send_event(me_at_touch);
	}
      else if(motion_event)
	{
	  me.type = EVENT_POINTER_MOVE;
	  send_event(me);
	}
      // Might have a second mouse button hit during init
      me_at_touch.mouse_buttons_pressed = me.mouse_buttons_pressed;
      break;
    case MOTION_STATE_HOLDING:
      // Exactly like MOTION_STATE_MOVING, except if we stay too close to the same spot it causes a HOLD event
      if(!me.mouse_buttons_pressed)
	{
	  motion_touch_state = MOTION_STATE_NONE;
	  me.type = EVENT_RELEASE;
	  me.mouse_buttons_pressed = me_at_touch.mouse_buttons_pressed;
	  send_event(me);
	  me.mouse_buttons_pressed = 0;
	}
      else if(me.mouse_buttons_pressed != me_at_touch.mouse_buttons_pressed)
	{
	  motion_touch_state = MOTION_STATE_ERROR;
	  unsigned char oldbuttons = me.mouse_buttons_pressed;
	  me.mouse_buttons_pressed = me_at_touch.mouse_buttons_pressed;
	  me.type = EVENT_RELEASE;
	  send_event(me);
	  me.mouse_buttons_pressed = oldbuttons;
	}
      else if(motion_exceeds_limit(me, me_at_touch, motion_to_break_hold))
	{	       
	  motion_touch_state = MOTION_STATE_MOVING;
	  me.type = EVENT_MOVE;
	  send_event(me);
	}
      if(motion_event)
	{
	  me.type = EVENT_POINTER_MOVE;
	  send_event(me);
	}
      break;
    case MOTION_STATE_MOVING:
      if(!me.mouse_buttons_pressed)
	{
	  motion_touch_state = MOTION_STATE_NONE;
	  me.type = EVENT_RELEASE;
	  me.mouse_buttons_pressed = me_at_touch.mouse_buttons_pressed;
	  send_event(me);
	  me.mouse_buttons_pressed = 0;
	}
      else if(me.mouse_buttons_pressed != me_at_touch.mouse_buttons_pressed)
	{
	  motion_touch_state = MOTION_STATE_ERROR;
	  unsigned char oldbuttons = me.mouse_buttons_pressed;
	  me.mouse_buttons_pressed = me_at_touch.mouse_buttons_pressed;
	  me.type = EVENT_RELEASE;
	  send_event(me);
	  me.mouse_buttons_pressed = oldbuttons;
	}
      if(motion_event)
	{
	  me.type = EVENT_MOVE;
	  send_event(me);
	}
      break;      
    case MOTION_STATE_ERROR:
      if(!me.mouse_buttons_pressed)
	{
	  motion_touch_state = MOTION_STATE_NONE;
	}
      else if(motion_event)
	{
	  me.type = EVENT_POINTER_MOVE;
	  send_event(me);
	}
      break;
      
    default:
      printf("ERROR in mouse input_event_handler state.  State is %d (%s).\n",
	     motion_touch_state, motion_touch_state_to_string(motion_touch_state));
      exit(20);
    }
  
  if(verbose)
    {
      if(motion_touch_state != old_motion_touch_state)
	{
	  printf("Device %s MOUSE motion_touch_state: %s -> %s\n",
		 filename,
		 motion_touch_state_to_string(old_motion_touch_state),
		 motion_touch_state_to_string(motion_touch_state));
	  old_motion_touch_state = motion_touch_state;
	}
    }
}


void input_event_listener::run()
{
  input_event  ie;
  pollfd pfd;

  memclear(ie);  
  
  for(;;)
    {
      int timeout_ms = 50;
      bool got_event = false;
      long long t_us;
      
      ///////////////////////////////////////////////////////////////////////////
      //
      // Timed events don't require an event read.  In fact, if there's
      // a real event that should go first.  So don't handle these when
      // there are real events.  When we run out of events, then
      // do the timed events before doing a poll that will wait.
      //
      ///////////////////////////////////////////////////////////////////////////

      pfd.fd = fd;
      pfd.events = POLLRDNORM;
      int result = poll(&pfd, 1, 0);

      if(result==1 && (pfd.revents&POLLERR))
	{
	  printf("Got poll error on device \"%s\".  Closing and repeatedly trying to reopen it.\n", filename);
	  close(fd);
	  fd = -1;
	  for(;;)
	    {
	      init();
	      if(fd!=-1)
		break;
	      sleep(5);
	    }
	  continue;
	}
      
	    
      if( (pfd.revents & POLLRDNORM) != POLLRDNORM )
	{
	  t_us = get_time_in_us();
	  me.time_in_us = t_us;
	  
	  if(motion_touch_state==MOTION_STATE_INIT && t_us - motion_event_start_us > wait_for_fingers_us)
	    {
	      motion_touch_state = MOTION_STATE_HOLDING;
	      me.count = 0;
	      me.type = EVENT_TOUCH;
	      send_event(me);
	    }
	  
	  if(motion_touch_state==MOTION_STATE_HOLDING &&  t_us - motion_event_start_us > (100000*(me.count+1)+wait_for_fingers_us))
	    {
	      me.count++;
	      me.type = EVENT_HOLD;
	      send_event(me);
	    }
	}

      
      pfd.fd = fd;
      pfd.events = POLLRDNORM;
      poll(&pfd, 1, timeout_ms);

      if( (pfd.revents & POLLRDNORM) == POLLRDNORM )
	{
	  got_event = true;
	  int num = read(fd, &ie, sizeof(ie));

	  if(num<=0)
	    {
	      printf("read error in input_event_listener().\n");
	      exit(20);
	    }

	  //if(verbose)
	  //  {
	  //    printf("READ INFO:  type=%d, code=%d, value=%d\n", ie.type, ie.code, ie.value);
	  //  }
	}
      else
	{
	  got_event = false;
	}      

      if(!got_event)
	continue;
      
      t_us = get_time_in_us();
      me.time_in_us = t_us;

      ///////////////////////////////////////////////////////////////////////////
      //
      // Normal events go after here.
      //
      ///////////////////////////////////////////////////////////////////////////

      if(verbose)
	{
	  printf("Device %s: ", filename);
	  print_event(ie);
	}

      if(ie.type==EV_REL)
	{
	  mouse = true;
	  touchscreen = false;
	}
	
      if(touchscreen)
	{
	  handle_touchscreen_motion(ie, t_us);
	  continue;
	}
      
      if(mouse)
	{
	  handle_mouse_motion(ie, t_us);
	  continue;	  
	}
      else
	{
	  //
	  // Keyboard key press
	  //
	  if(ie.type==EV_KEY)
	    {
	      //printf("In input_event_handler for key.\n");
	      if(key_press_state==KEY_STATE_NONE)
		{
		  if(ie.value)
		    {
		      me.key_code = ie.code;
		      me.key_ascii = keycode_to_ascii(me.key_code);
		      key_event_start_us = t_us;
		      key_press_event = true;
		    }
		}
	      else if(key_press_state==KEY_STATE_PRESSED)
		{
		  if(!ie.value && ie.code==me.key_code)
		    {
		      me.key_code = ie.code;
		      me.key_ascii = keycode_to_ascii(me.key_code);
		      key_release_event = true;
		    }
		}
	    }

	  if (ie.type == EV_SYN && ie.code == SYN_REPORT)
	    {	      
	      if(key_press_event)
		{
		  key_press_state = KEY_STATE_PRESSED;
		  me.type = EVENT_KEY_PRESS;
		  key_press_event = false;
		  key_release_event = false;
		  send_event(me);
		  key_press_count=0;
		  continue;
		}
	      else if(key_release_event)
		{
		  key_press_state = KEY_STATE_NONE;
		  me.type = EVENT_KEY_RELEASE;
		  key_press_event = false;
		  key_release_event = false;
		  send_event(me);
		  continue;
		}
	      else if(key_press_state==KEY_STATE_PRESSED && t_us - key_event_start_us > (100000*(key_press_count+1)))
		{
		  key_press_count++;
		  me.count=key_press_count;
		  me.type = EVENT_KEY_HOLD;
		  send_event(me);
		}
	    }
	}      
    }
}

//delta time: 6.0ms               type:EV_ABS          code:ABS_MT_SLOT value:0
//delta time: 0.0ms               type:EV_ABS   code:ABS_MT_TRACKING_ID value:-1
//delta time: 0.0ms               type:EV_ABS          code:ABS_MT_SLOT value:1
//delta time: 0.0ms               type:EV_ABS    code:ABS_MT_POSITION_X value:8584


//delta time: 6.0ms               type:EV_ABS          code:ABS_MT_SLOT value:0
//delta time: 0.0ms               type:EV_ABS    code:ABS_MT_POSITION_X value:9458
//delta time: 0.0ms               type:EV_ABS    code:ABS_MT_POSITION_Y value:8541
//delta time: 0.0ms               type:EV_ABS          code:ABS_MT_SLOT value:1
//delta time: 0.0ms               type:EV_ABS    code:ABS_MT_POSITION_X value:8814
//delta time: 0.0ms               type:EV_ABS    code:ABS_MT_POSITION_Y value:14598
//delta time: 0.0ms               type:EV_ABS                code:ABS_X value:9458
//delta time: 0.0ms               type:EV_ABS                code:ABS_Y value:8541
//delta time: 0.0ms               type:EV_MSC        code:MSC_TIMESTAMP value:64100 (timestamp delta time  4.3ms)
//delta time: 0.0ms               type:EV_SYN           code:SYN_REPORT value:0




bool strings_match_to_length_of_first(const char* s1, const char* s2)
{
  while(*s1 && *s1==*s2)
    {
      s1++;
      s2++;
    }

  if(!*s1)
    return true;

  return false;
}



void input_event_handler::scan_for_input_devices()
{
  //printf("Scanning for input devices.\n");
  
  DIR* dp = opendir("/dev/input");

  if(!dp)
    {
      printf("Can't search directory \"/dev/input\".\n");
      return;
    }

  //printf("Listening to event files:\n\n");
  
  for(;;)
    {
      bool new_device = true;
      dirent *de = readdir(dp);

      if(!de)
        break;

      if(strings_match_to_length_of_first("event", de->d_name))
        {
	  char ename[200];
          //printf("  %s\n", de->d_name);
          sprintf(ename, "/dev/input/%.100s", de->d_name);

	  // check to see if listener already exists.  If so, moce on to the next.
	  for(int i=0; i<num_listeners; i++)
	    {
	      if(!strcmp(ename, listeners[i]->filename))
		{
		  new_device = false;
		  break;
		}
	    }

	  if(new_device)
	    {
	      printf("Found new input device \"%s\".\n", ename);
	      listeners[num_listeners++] = new input_event_listener(ename, maxx, maxy, this);
	    }
	}
    }

  closedir(dp);
}

void input_event_handler::run()
{
  for(;;)
    {
      scan_for_input_devices();
      sleep(5);
    }
}



input_event_handler::input_event_handler(int maxx, int maxy, display* disp)
{
  // Scan for new devices in /dev/input/event* and create listeners for them. 
  
  // struct dirent {
  //   ino_t          d_ino;       /* Inode number */
  //   off_t          d_off;       /* Not an offset; see below */
  //   unsigned short d_reclen;    /* Length of this record */
  //   unsigned char  d_type;      /* Type of file; not supported
  //                                  by all filesystem types */
  //   char           d_name[256]; /* Null-terminated filename */
  // };

  this->maxx = maxx;
  this->maxy = maxy;
  this->disp = disp;

  current_listener = 0;
  num_listeners = 0;
  
  start();
}


input_event_handler::~input_event_handler()
{
  for(int i=0; i<num_listeners; i++)
    {
      delete listeners[i];
    }
}



char keycode_to_ascii(int code)
{
  bool do_init = true;
  const static int MAXC = 100;
  static char code_to_key[MAXC];

  if(do_init)
    {
      for(int i=0; i<MAXC; i++)
	code_to_key[i] = 0;
      
      code_to_key[KEY_0] = '0';
      code_to_key[KEY_1] = '1';
      code_to_key[KEY_2] = '2';
      code_to_key[KEY_3] = '3';
      code_to_key[KEY_4] = '4';
      code_to_key[KEY_5] = '5';
      code_to_key[KEY_6] = '6';
      code_to_key[KEY_7] = '7';
      code_to_key[KEY_8] = '8';
      code_to_key[KEY_9] = '9';
      code_to_key[KEY_A] = 'A';
      code_to_key[KEY_B] = 'B';
      code_to_key[KEY_C] = 'C';
      code_to_key[KEY_D] = 'D';
      code_to_key[KEY_E] = 'E';
      code_to_key[KEY_F] = 'F';
      code_to_key[KEY_G] = 'G';
      code_to_key[KEY_H] = 'H';
      code_to_key[KEY_I] = 'I';
      code_to_key[KEY_J] = 'J';
      code_to_key[KEY_K] = 'K';
      code_to_key[KEY_L] = 'L';
      code_to_key[KEY_M] = 'M';
      code_to_key[KEY_N] = 'N';
      code_to_key[KEY_O] = 'O';
      code_to_key[KEY_P] = 'P';
      code_to_key[KEY_Q] = 'Q';
      code_to_key[KEY_R] = 'R';
      code_to_key[KEY_S] = 'S';
      code_to_key[KEY_T] = 'T';
      code_to_key[KEY_U] = 'U';
      code_to_key[KEY_V] = 'V';
      code_to_key[KEY_W] = 'W';
      code_to_key[KEY_X] = 'X';
      code_to_key[KEY_Y] = 'Y';
      code_to_key[KEY_Z] = 'Z';
      code_to_key[KEY_MINUS]      = '-';
      code_to_key[KEY_EQUAL]      = '=';
      code_to_key[KEY_TAB]        = '\t';
      code_to_key[KEY_LEFTBRACE]  = '[';
      code_to_key[KEY_RIGHTBRACE] = ']';
      code_to_key[KEY_ENTER]      = '\n';
      code_to_key[KEY_SEMICOLON]  = ';';
      code_to_key[KEY_APOSTROPHE] = '\'';
      code_to_key[KEY_GRAVE]      = '`';
      code_to_key[KEY_BACKSLASH]  = '\\';
      code_to_key[KEY_COMMA]      = ',';
      code_to_key[KEY_DOT]        = '.';
      code_to_key[KEY_SLASH]      = '/';
      code_to_key[KEY_KPASTERISK] = '*';
      code_to_key[KEY_SPACE]      = ' ';
      code_to_key[KEY_KP0]        = '0';
      code_to_key[KEY_KP1]        = '1';
      code_to_key[KEY_KP2]        = '2';
      code_to_key[KEY_KP3]        = '3';
      code_to_key[KEY_KP4]        = '4';
      code_to_key[KEY_KP5]        = '5';
      code_to_key[KEY_KP6]        = '6';
      code_to_key[KEY_KP7]        = '7';
      code_to_key[KEY_KP8]        = '8';
      code_to_key[KEY_KP9]        = '9';
      code_to_key[KEY_KPDOT]      = '.';
      code_to_key[KEY_KPPLUS]     = '+';
      code_to_key[KEY_KPMINUS]    = '-';
      code_to_key[KEY_KPSLASH]    = '/';
    }
  
  if(code<0||code>=MAXC)
    return 0;
  
  return code_to_key[code];
}



void input_event_handler::set_verbose()
{
  for(int i=0; i<num_listeners; i++)
    listeners[i]->verbose = true;
}


void input_event_listener::send_event(my_event& me)
{
  if(verbose)
    {
      printf("Sending event: ");
      ::print_event(me);
    }
  
  ieh->out.lock();
  while(!ieh->out.data.put_event(me))
    ieh->out.wait_for_change();
  ieh->out.unlock();
}


// Will sleep until some event happens                                                                                                      
my_event input_event_handler::wait_for_event()
{
  my_event me;
  out.lock();
  for(;;)
    {
      me = out.data.get_event();
      if(me.type!=EVENT_NONE)
        break;
      out.wait_for_change();
    }
  out.unlock();

  return me;
}


// Returns immediately; event may be EVENT_NONE                                                                                             
my_event input_event_handler::check_for_event()
{
  out.lock();
  my_event me = out.data.get_event();
  out.unlock();

  return me;
}


