From 19ae8b55d1e4869c8ec86ed820f3de113efcd751 Mon Sep 17 00:00:00 2001
From: Ingo Ruhnke <grumbel@gmx.de>
Date: Tue, 28 Dec 2010 17:38:12 +0100
Subject: [PATCH] Implemnted some AxisFilter

---
 SConstruct          |   4 +-
 TODO                |  31 +++++++----
 Xbox360             |  58 ++++++++++++++++++---
 src/axis_event.cpp  |  29 +++++++----
 src/axis_event.hpp  |  14 +++--
 src/axis_filter.cpp | 123 +++++++++++++++++++++++++++++++++++++++++++-
 src/axis_filter.hpp |  34 +++++++++++-
 src/uinput.cpp      |   5 +-
 8 files changed, 257 insertions(+), 41 deletions(-)

diff --git a/SConstruct b/SConstruct
index 0a20e8b..d61f74f 100644
--- a/SConstruct
+++ b/SConstruct
@@ -1,6 +1,6 @@
 # -*- python -*-
 
-if True:
+if False:
     env = Environment(CPPFLAGS=['-g', '-O2', '-Wall', '-ansi', '-pedantic'])
 else:
     env = Environment(CXXFLAGS= [ "-O3", "-g3",
@@ -9,7 +9,7 @@ else:
                                   "-Wall",
                                   "-Wextra",
                                   "-Wnon-virtual-dtor",
-                                  "-Weffc++",
+                                  #"-Weffc++",
                                   #"-Wconversion",
                                   "-Wold-style-cast",
                                   # "-Werror",
diff --git a/TODO b/TODO
index 4653b70..a4bb642 100644
--- a/TODO
+++ b/TODO
@@ -27,6 +27,18 @@ $ dput my-ppa xboxdrv_0.6.1_source.changes
 Stuff to do before 0.6.1 release:
 =================================
 
+* "couldn't convert 'ABS_y' to enum, not a member of EV_ABS"
+ 
+  convert all enum names to uppercase? or does that lead to conflicts in the naming?
+
+* allow giving ini options on command line: xboxdrv '-e' foo=5 (or '-o')
+
+* allow the right side of --ui-buttomap/--ui-axismap to be empty:
+
+  xboxdrv --ui-buttonmap X1^deadzone:50
+
+  This would just add the filter to the chain, not create a complete new mapping
+
 * remove guide button from default mapping when its an Xbox1 controller
 
 * fixup guitar
@@ -37,6 +49,15 @@ Stuff to do before 0.6.1 release:
 
 * add more example scripts
 
+* make evdev grab optional:
+
+  --evdev-grab
+
+    Takes exclusive ownership of the event device, all events are
+    directed to xboxdrv and no other application will be able to
+    receive events.
+
+  --evdev-no-grab
 
 Stuff to do before 0.7.0 release:
 =================================
@@ -51,16 +72,6 @@ Stuff to do before 0.7.0 release:
 
 * make dummy joystick axis creation optional
 
-* make evdev grab optional:
-
-  --evdev-grab
-
-    Takes exclusive ownership of the event device, all events are
-    directed to xboxdrv and no other application will be able to
-    receive events.
-
-  --evdev-no-grab
-
 * --ui-axismap LT=KEY_A:KEY_B:1 
 
 Here KEY_B is the key you want to send and KEY_A is a random other key
diff --git a/Xbox360 b/Xbox360
index b184f14..bd37753 100644
--- a/Xbox360
+++ b/Xbox360
@@ -23,11 +23,55 @@
 |                `---.    .---'                        \ \      / /          |
 |                    | \/ |                             \ `----' /           | 
 |                    `----'                              `------'            |
-`                    DD/DPAD_Y-                              Y2-             '
- |                               _____________                              | 
-  .                           ,-'             '-.                           .         
-   \                        ,/                   \.                        /
-    \.                    ,/                       \.                    ./   
-      \__              __/                           \__              __/     
-         \____________/                                 \____________/                   
+`                   DD/DPAD_Y-                              Y2-             '
+ |                               ___________                              | 
+  .                           ,-'           '-.                           .         
+   \                        ,/                 \.                        /
+    \.                    ,/                     \.                    ./   
+      \__              __/                         \__              __/     
+         \____________/                               \____________/                   
  
+        
+        ||
+        ||
+
+       ====                 _______Reload
+      ,----------------    /  ______Fire
+     /  _                 |  /  
+    /  / \     o O o      | O      
+   /   \_/                O   O
+  /        ^                O  \__Reload
+ |       <-|->               \____ Jump
+ |         v                                 
+                                                                                |||||||||||||
+ .             .                                                            
+
+
+                  /
+                 |
+                 .
+                  \_____
+
+                        ____
+           ___                          .'    `.
+4 lines:  /   \   5 lines:  6 lines:   /        \
+         |     |                      |          |
+          \___/                        \        /
+
+
+                           
+         .----.   
+        /      \            Guide    
+       |        |  Back     ,--.    Start               ,-.         
+        \      /   ( < )   / \/ \   ( > )              ( Y )        
+         `----'            \ /\ /                   ,-. `-' ,-.     
+                            `--'                   ( X )   ( B )    
+                                                    `-' ,-. `-'      
+                          ,----.                       ( A )        
+                          | /\ |                        `-'         
+                      ,---'    `---. 
+                      | <        > | 
+                      `---.    .---' 
+                          | \/ |     
+                          `----'     
+   
diff --git a/src/axis_event.cpp b/src/axis_event.cpp
index 24d1a09..8e2fc18 100644
--- a/src/axis_event.cpp
+++ b/src/axis_event.cpp
@@ -80,6 +80,8 @@ AxisEvent::from_string(const std::string& str)
 }
 
 AxisEvent::AxisEvent(AxisEventHandler* handler) :
+  m_min(0),
+  m_max(0),
   m_handler(handler),
   m_filters()
 {
@@ -98,14 +100,14 @@ AxisEvent::init(uInput& uinput) const
 }
 
 void
-AxisEvent::send(uInput& uinput, int old_value, int value) const
+AxisEvent::send(uInput& uinput, int value)
 {
   for(std::vector<AxisFilterPtr>::const_iterator i = m_filters.begin(); i != m_filters.end(); ++i)
   {
-    value = (*i)->filter(old_value, value);
+    value = (*i)->filter(value, m_min, m_max);
   }
 
-  m_handler->send(uinput, old_value, value);
+  m_handler->send(uinput, value);
 }
 
 void
@@ -117,6 +119,8 @@ AxisEvent::update(uInput& uinput, int msec_delta)
 void
 AxisEvent::set_axis_range(int min, int max)
 {
+  m_min = min;
+  m_max = max;
   m_handler->set_axis_range(min, max);
 }
 
@@ -186,7 +190,7 @@ RelAxisEventHandler::init(uInput& uinput) const
 }
 
 void
-RelAxisEventHandler::send(uInput& uinput, int old_value, int value) const
+RelAxisEventHandler::send(uInput& uinput, int value)
 {
   // FIXME: Need to know the min/max of value
   int v = m_value * value / 32767;
@@ -278,7 +282,7 @@ AbsAxisEventHandler::init(uInput& uinput) const
 }
 
 void
-AbsAxisEventHandler:: send(uInput& uinput, int old_value, int value) const
+AbsAxisEventHandler:: send(uInput& uinput, int value)
 {
   /*FIXME for(std::vector<AxisFilterPtr>::const_iterator i = m_filters.begin(); i != m_filters.end(); ++i)
   {
@@ -355,7 +359,8 @@ KeyAxisEventHandler::from_string(const std::string& str)
   return ev.release();
 }
 
-KeyAxisEventHandler::KeyAxisEventHandler()
+KeyAxisEventHandler::KeyAxisEventHandler() :
+  m_old_value(0)
 {
   std::fill_n(m_up_codes,   MAX_MODIFIER+1, UIEvent::invalid());
   std::fill_n(m_down_codes, MAX_MODIFIER+1, UIEvent::invalid());
@@ -380,10 +385,10 @@ KeyAxisEventHandler::init(uInput& uinput) const
 }
 
 void
-KeyAxisEventHandler::send(uInput& uinput, int old_value, int value) const
+KeyAxisEventHandler::send(uInput& uinput, int value)
 {
-  if (::abs(old_value) <  m_threshold &&
-      ::abs(value)     >= m_threshold)
+  if (::abs(m_old_value) <  m_threshold &&
+      ::abs(value)       >= m_threshold)
   { // entering bigger then threshold zone
     if (value < 0)
     {
@@ -402,8 +407,8 @@ KeyAxisEventHandler::send(uInput& uinput, int old_value, int value) const
         uinput.send_key(m_up_codes[i].device_id, m_up_codes[i].code, false);
     }
   }
-  else if (::abs(old_value) >= m_threshold &&
-           ::abs(value)     <  m_threshold)
+  else if (::abs(m_old_value) >= m_threshold &&
+           ::abs(value)       <  m_threshold)
   { // entering zero zone
     for(int i = 0; m_up_codes[i].is_valid(); ++i)
       uinput.send_key(m_down_codes[i].device_id, m_down_codes[i].code, false);
@@ -411,6 +416,8 @@ KeyAxisEventHandler::send(uInput& uinput, int old_value, int value) const
     for(int i = 0; m_up_codes[i].is_valid(); ++i)
       uinput.send_key(m_up_codes[i].device_id, m_up_codes[i].code, false);
   }
+
+  m_old_value = value;
 }
 
 void
diff --git a/src/axis_event.hpp b/src/axis_event.hpp
index 916f3d1..e244f22 100644
--- a/src/axis_event.hpp
+++ b/src/axis_event.hpp
@@ -51,7 +51,7 @@ public:
   void set_filters(const std::vector<AxisFilterPtr>& filters);
 
   void init(uInput& uinput) const;
-  void send(uInput& uinput, int old_value, int value) const;
+  void send(uInput& uinput, int value);
   void update(uInput& uinput, int msec_delta);
 
   void set_axis_range(int min, int max);
@@ -59,6 +59,8 @@ public:
   std::string str() const;
 
 private:
+  int m_min;
+  int m_max;
   boost::scoped_ptr<AxisEventHandler> m_handler;
   std::vector<AxisFilterPtr> m_filters;
 };
@@ -69,7 +71,7 @@ public:
   virtual ~AxisEventHandler() {}
 
   virtual void init(uInput& uinput) const =0;
-  virtual void send(uInput& uinput, int old_value, int value) const =0;
+  virtual void send(uInput& uinput, int value) =0;
   virtual void update(uInput& uinput, int msec_delta) =0;
 
   virtual void set_axis_range(int min, int max) {}
@@ -87,7 +89,7 @@ public:
   RelAxisEventHandler(int device_id, int code, int repeat = 10, float value = 5);
 
   void init(uInput& uinput) const;
-  void send(uInput& uinput, int old_value, int value) const;
+  void send(uInput& uinput, int value);
   void update(uInput& uinput, int msec_delta);
 
   std::string str() const;
@@ -110,7 +112,7 @@ public:
   void set_axis_range(int min, int max);
 
   void init(uInput& uinput) const;
-  void send(uInput& uinput, int old_value, int value) const;
+  void send(uInput& uinput, int value);
   void update(uInput& uinput, int msec_delta);
 
   std::string str() const;
@@ -132,7 +134,7 @@ public:
   KeyAxisEventHandler();
 
   void init(uInput& uinput) const;
-  void send(uInput& uinput, int old_value, int value) const;
+  void send(uInput& uinput, int value);
   void update(uInput& uinput, int msec_delta);
 
   std::string str() const;
@@ -140,6 +142,8 @@ public:
 private:
   static const int MAX_MODIFIER = 4;
 
+  int m_old_value;
+
   // Array is terminated by -1
   UIEvent m_up_codes[MAX_MODIFIER+1];
   UIEvent m_down_codes[MAX_MODIFIER+1];
diff --git a/src/axis_filter.cpp b/src/axis_filter.cpp
index b353bf1..a4bed92 100644
--- a/src/axis_filter.cpp
+++ b/src/axis_filter.cpp
@@ -18,20 +18,50 @@
 
 #include "axis_filter.hpp"
 
+#include <boost/lexical_cast.hpp>
+#include <boost/tokenizer.hpp>
 #include <iostream>
+#include <math.h>
 #include <sstream>
 #include <stdexcept>
+
+#include "helper.hpp"
+
+namespace {
+
+/** converts the arbitary range to [-1,1] */
+inline float to_float(int value, int min, int max)
+{
+  return static_cast<float>(value - min) / static_cast<float>(max - min) * 2.0f - 1.0f;
+}
+
+/** converts the range [-1,1] to [min,max] */
+inline int from_float(float value, int min, int max)
+{
+  return (value + 1.0f) / 2.0f * static_cast<float>(max - min) + min;
+}
+
+} // namespace
 
 AxisFilterPtr
 AxisFilter::from_string(const std::string& str)
 {
   std::string::size_type p = str.find(':');
   const std::string& filtername = str.substr(0, p);
+  std::string rest = str.substr(p+1);
 
   if (filtername == "invert")
   {
     return AxisFilterPtr(new InvertAxisFilter);
   }
+  else if (filtername == "calibration" || filtername == "cal")
+  {
+    return AxisFilterPtr(CalibrationAxisFilter::from_string(rest));
+  }
+  else if (filtername == "sensitivity" || filtername == "sen")
+  {
+    return AxisFilterPtr(SensitivityAxisFilter::from_string(rest));
+  }
   else
   {
     std::ostringstream out;
@@ -41,9 +71,100 @@ AxisFilter::from_string(const std::string& str)
 }
 
 int
-InvertAxisFilter::filter(int old_value, int value)
+InvertAxisFilter::filter(int value, int min, int max)
 {
+  // FIXME: take min/max into account
   return -value;
 }
 
+SensitivityAxisFilter*
+SensitivityAxisFilter::from_string(const std::string& str)
+{
+  typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+  tokenizer tokens(str, boost::char_separator<char>(":", "", boost::keep_empty_tokens));
+  
+  float sensitivity = 0.0f;
+
+  int j = 0;
+  for(tokenizer::iterator i = tokens.begin(); i != tokens.end(); ++i, ++j)
+  {
+    switch(j)
+    {
+      case 0: sensitivity = boost::lexical_cast<float>(*i); break;
+      default: throw std::runtime_error("to many arguments");
+    };
+  }
+
+  return new SensitivityAxisFilter(sensitivity);
+}
+
+SensitivityAxisFilter::SensitivityAxisFilter(float sensitivity) :
+  m_sensitivity(sensitivity)
+{  
+}
+
+int
+SensitivityAxisFilter::filter(int value, int min, int max)
+{
+  float pos = to_float(value, min, max);
+
+  float t = powf(2, m_sensitivity);
+
+  if (pos > 0)
+  {
+    pos = powf(1.0f - powf(1.0f - pos, t), 1 / t);
+    return from_float(pos, min, max);
+  }
+  else
+  {
+    pos = powf(1.0f - powf(1.0f - -pos, t), 1 / t);
+    return from_float(-pos, min, max);
+  }
+}
+
+CalibrationAxisFilter*
+CalibrationAxisFilter::from_string(const std::string& str)
+{
+  typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+  tokenizer tokens(str, boost::char_separator<char>(":", "", boost::keep_empty_tokens));
+
+  int min    = 0;
+  int center = 0;
+  int max    = 0;
+
+  int j = 0;
+  for(tokenizer::iterator i = tokens.begin(); i != tokens.end(); ++i, ++j)
+  {
+    switch(j)
+    {
+      case 0: min    = boost::lexical_cast<int>(*i); break;
+      case 1: center = boost::lexical_cast<int>(*i); break;
+      case 2: max    = boost::lexical_cast<int>(*i); break;
+      default: throw std::runtime_error("to many arguments");
+    };
+  }
+
+  return new CalibrationAxisFilter(min, center, max);
+}
+
+CalibrationAxisFilter::CalibrationAxisFilter(int min, int center, int max) :
+  m_min(min),
+  m_center(center),
+  m_max(max)
+{
+}
+
+int
+CalibrationAxisFilter::filter(int value, int min, int max)
+{
+  if (value < m_center)
+    value = -min * (value - m_center) / (m_center - m_min);
+  else if (value > m_center)
+    value = max * (value - m_center) / (m_max - m_center);
+  else
+    value = 0;
+
+  return Math::clamp(min, value, max);
+}
+
 /* EOF */
diff --git a/src/axis_filter.hpp b/src/axis_filter.hpp
index 54a595d..70a7efa 100644
--- a/src/axis_filter.hpp
+++ b/src/axis_filter.hpp
@@ -35,7 +35,7 @@ public:
   AxisFilter() {}
   virtual ~AxisFilter() {}
 
-  virtual int filter(int old_value, int value) =0;
+  virtual int filter(int value, int min, int max) =0;
 };
 
 class InvertAxisFilter : public AxisFilter
@@ -44,7 +44,37 @@ public:
   InvertAxisFilter() {}
   ~InvertAxisFilter() {}
 
-  int filter(int old_value, int value);
+  int filter(int value, int min, int max);
+};
+
+class SensitivityAxisFilter : public AxisFilter
+{
+public:
+  static SensitivityAxisFilter* from_string(const std::string& str);
+
+public:
+  SensitivityAxisFilter(float sensitivity);
+
+  int filter(int value, int min, int max);
+
+private:
+  float m_sensitivity;
+};
+
+class CalibrationAxisFilter : public AxisFilter
+{
+public:
+  static CalibrationAxisFilter* from_string(const std::string& str);
+
+public:
+  CalibrationAxisFilter(int min, int center, int max);
+
+  int filter(int value, int min, int max);
+
+private:
+  int m_min;
+  int m_center;
+  int m_max;
 };
 
 #endif
diff --git a/src/uinput.cpp b/src/uinput.cpp
index 34fa5e4..bd66ee1 100644
--- a/src/uinput.cpp
+++ b/src/uinput.cpp
@@ -490,7 +490,6 @@ uInput::send_axis(XboxAxis code, int32_t value)
   // not just when the axis changed
   if (axis_state[code] != value)
   {
-    int old_value = axis_state[code];
     axis_state[code] = value;
 
     bool event_send = false;
@@ -503,7 +502,7 @@ uInput::send_axis(XboxAxis code, int32_t value)
         const AxisEventPtr& event = cfg.get_axis_map().lookup(static_cast<XboxButton>(shift), code);
         if (event)
         {
-          event->send(*this, old_value, value);
+          event->send(*this, value);
           event_send = true;
         }
       }
@@ -515,7 +514,7 @@ uInput::send_axis(XboxAxis code, int32_t value)
       const AxisEventPtr& event = cfg.get_axis_map().lookup(code);
       if (event)
       {
-        event->send(*this, old_value, value);
+        event->send(*this, value);
       }
     }
   }