Implement event capture.
authorbruceperens <bruceperens@01035d8c-6547-0410-b346-abe4f91aad63>
Sat, 19 Apr 2014 00:37:30 +0000 (00:37 +0000)
committerbruceperens <bruceperens@01035d8c-6547-0410-b346-abe4f91aad63>
Sat, 19 Apr 2014 00:37:30 +0000 (00:37 +0000)
git-svn-id: https://svn.code.sf.net/p/freetel/code@1509 01035d8c-6547-0410-b346-abe4f91aad63

freedv-server/source/platform/linux/evdev.cpp
freedv-server/source/platform/linux/evdev.h
freedv-server/source/platform/linux/ptt_evdev.cpp

index 429cd3c8b8e8c9b6019e3a4f3b36dec9c47ce31e..8c45a5298616f4c816d0d67483aaf77a599d0c2a 100644 (file)
@@ -13,8 +13,8 @@
 #include <assert.h>
 
 namespace FreeDV {
-  static bool
-  bit_set(unsigned int bit, const uint8_t * field)
+  inline static bool
+  bit_is_set(unsigned int bit, const uint8_t * field)
   {
     return ((field[bit / 8] & (1 << (bit % 8))) != 0);
   }
@@ -69,6 +69,18 @@ namespace FreeDV {
     return string;
   }
   
+  bool
+  EvDev::button_state(unsigned int index)
+  {
+    uint8_t    buttons[(KEY_MAX + 7) / 8];
+
+    
+    if ( ioctl(fd, EVIOCGKEY(sizeof(buttons)), buttons) < 0 )
+      do_throw(errno, name, special_file, "ioctl EVIOCGKEY");
+
+    return bit_is_set(index, buttons);
+  }
+
   void
   EvDev::delete_enumeration(
    EvDev::device_enumeration * data,
@@ -140,7 +152,7 @@ namespace FreeDV {
   
     device.name = strdup(str.str().c_str());
   
-    if ( bit_set(EV_KEY, device.event_types) == 0
+    if ( bit_is_set(EV_KEY, device.event_types) == 0
     || ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(device.buttons)), device.buttons)
      < 0 )
       memset(device.buttons, 0, sizeof(device.buttons));
@@ -148,6 +160,17 @@ namespace FreeDV {
     return true;
   }
   
+  bool
+  EvDev::has_button(unsigned int index)
+  {
+    uint8_t    buttons[(KEY_MAX + 7) / 8];
+
+    if ( ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(buttons)), buttons) < 0 )
+      return false;
+
+    return bit_is_set(index, buttons);
+  }
+
   int
   EvDev::poll_fds(PollType * array, int length)
   {
@@ -167,7 +190,7 @@ namespace FreeDV {
     if ( ioctl(fd, FIONREAD, &length) < 0 )
       do_throw(errno, name, special_file, "ioctl FIONREAD");
 
-    return length;
+    return length / sizeof(input_event);
   }
 
   std::size_t
index d8d3589b7155d6d08db5278b9432201067d09dc1..52d11a8c7ff5adb316511b53edbc87f95f393c0f 100644 (file)
@@ -26,6 +26,8 @@ namespace FreeDV {
                         struct device_enumeration &    device);
   
   public:
+    bool               button_state(unsigned int index);
+
     static void                delete_enumeration(
                         device_enumeration *   data,
                         std::size_t            count);
@@ -33,12 +35,18 @@ namespace FreeDV {
     static device_enumeration *
                        enumerate(std::size_t & count);
   
+    bool               has_button(unsigned int index);
+
+    const char *       long_name() { return name; }
+
     int                        poll_fds(PollType *, int);
 
     std::size_t                ready();
 
     std::size_t                read_events(input_event * data, std::size_t count);
 
+    const char *       device_name() { return special_file; }
+
                        EvDev(const char * name);
                        ~EvDev();
   };
index 0263329f4c03da2777de83052584587b9a5ace99..08f3c16eb08c00a755297ec113e88f4428b7f2f4 100644 (file)
@@ -5,24 +5,51 @@
 ///
 
 #include "evdev.h"
+#include <stdlib.h>
+#include <string.h>
 #include <iostream>
 #include <stdexcept>
-
-static bool
-bit_set(unsigned int bit, const uint8_t * field)
-{
-  return ((field[bit / 8] & (1 << (bit % 8))) != 0);
-}
+#include <sstream>
 
 namespace FreeDV {
+  NORETURN static void
+  do_throw(
+   const char * name,
+   const char * message = 0)
+  {
+    std::ostringstream str;
+
+    str << "EVDEV ";
+
+    str << '\"' << name << "\": ";
+    if ( message ) {
+      str << message;
+    }
+
+    str << '.';
+    throw std::runtime_error(str.str().c_str());
+  }
+
+  inline static bool
+  bit_is_set(unsigned int bit, const uint8_t * field)
+  {
+    return ((field[bit / 8] & (1 << (bit % 8))) != 0);
+  }
+  
   /// PTT driver using Linux evdev.
   ///
   class PTT_EvDev : public PTTInput {
   private:
-    /// This is true if ready has not yet been sent.
-    ///
+    EvDev              * dev;
+    int                        button_index;
     bool               pressed;
-    bool               ready_one_shot;
+    bool               changed;
+
+                       PTT_EvDev(const PTT_EvDev &);
+                       PTT_EvDev & operator =(const PTT_EvDev &);
+
+    void               process_events();
+
   public:
     /// Instantiate.
     ///
@@ -37,7 +64,7 @@ namespace FreeDV {
     /// \return The number of file descriptors written to the array.
     virtual int        poll_fds(PollType * array, int space);
 
-    /// Return the amount of bytes ready for read.
+    /// Return the amount of events ready for read.
     ///
     std::size_t        ready();
 
@@ -47,33 +74,90 @@ namespace FreeDV {
   };
 
   PTT_EvDev::PTT_EvDev(const char * _parameters)
-  : PTTInput("constant", _parameters), pressed(false), ready_one_shot(true)
+  : PTTInput("evdev", _parameters), dev(0), button_index(0), pressed(false),
+    changed(false)
   {
+    char *     p = strdup(parameters);
+    char *     number = index(p, ',');
+
+    if ( number == 0 )
+      do_throw(
+       parameters,
+       "The device name must be followed by a comma and a button number."); 
+
+    *number++ = '\0';
+
+    button_index = atoi(number);
+    
+    dev = new EvDev(p);
+    delete p;
+
+    if ( !dev->has_button(button_index) ) {
+      std::ostringstream str;
+
+      str << "There is no button " << button_index << " in this device.";
+
+      do_throw(parameters, str.str().c_str());
+    }
+
+    pressed = dev->button_state(button_index);
   }
 
   PTT_EvDev::~PTT_EvDev()
   {
+    delete dev;
   }
 
   int
-  PTT_EvDev::poll_fds(PollType *, int)
+  PTT_EvDev::poll_fds(PollType * array, int count)
   {
-    return 0;
+    return dev->poll_fds(array, count);
+  }
+
+  void
+  PTT_EvDev::process_events()
+  {
+    while ( dev->ready() > 0 ) {
+      input_event      events[10];
+
+      const std::size_t count = dev->read_events(
+       events,
+       sizeof(events) / sizeof(*events));
+
+      for ( std::size_t i = 0; i < count; i++ ) {
+        const input_event * const event = &events[i];
+        if ( event->type == EV_KEY && event->code == button_index ) {
+          switch ( event->value ) {
+          case 0:
+            if ( pressed )
+             changed = true;
+           pressed = false;
+            break;
+          case 1:
+            if ( !pressed )
+             changed = true;
+            pressed = true;
+            break;
+         default:
+           ;
+         }
+        }
+      }
+    }
   }
 
   std::size_t
   PTT_EvDev::ready()
   {
-    if ( ready_one_shot )
-      return 1;
-    else
-      return 0;
+    return dev->ready();
   }
 
   bool
   PTT_EvDev::state()
   {
-    return false;
+    process_events();
+    changed = false;
+    return pressed;
   }
 
   PTTInput *
@@ -89,18 +173,18 @@ namespace FreeDV {
     EvDev::device_enumeration * const  devices = EvDev::enumerate(count);
   
     for ( std::size_t i = 0; i < count; i++ ) {
-      if ( bit_set(EV_KEY, devices[i].event_types) ) {
+      if ( bit_is_set(EV_KEY, devices[i].event_types) ) {
         int    low = -1;
         int    high = -1;
 
         for ( int j = KEY_F1; j <= KEY_MAX; j++ ) {
-          if ( bit_set(j, devices[i].buttons) ) {
+          if ( bit_is_set(j, devices[i].buttons) ) {
             low = j;
             break;
           }
         }
         for ( int j = KEY_MAX; j >= KEY_F1; j-- ) {
-          if ( bit_set(j, devices[i].buttons) ) {
+          if ( bit_is_set(j, devices[i].buttons) ) {
             high = j;
             break;
           }