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); } } }