// Copyright (C) 2010 Dolphin Project. // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, version 2.0. // This program 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. See the // GNU General Public License 2.0 for more details. // A copy of the GPL 2.0 should have been included with the program. // If not, see http://www.gnu.org/licenses/ // Official SVN repository and contact information can be found at // http://code.google.com/p/dolphin-emu/ #ifndef _CONTROLLEREMU_H_ #define _CONTROLLEREMU_H_ // windows crap #define NOMINMAX #include #include #include #include #include "GCPadStatus.h" #include "pluginspecs_wiimote.h" #include "ControllerInterface/ControllerInterface.h" #include "IniFile.h" #define sign(x) ((x)?(x)<0?-1:1:0) enum { GROUP_TYPE_OTHER, GROUP_TYPE_STICK, GROUP_TYPE_MIXED_TRIGGERS, GROUP_TYPE_BUTTONS, GROUP_TYPE_FORCE, GROUP_TYPE_EXTENSION, GROUP_TYPE_TILT, GROUP_TYPE_CURSOR, GROUP_TYPE_TRIGGERS, GROUP_TYPE_UDPWII }; const char * const named_directions[] = { "Up", "Down", "Left", "Right" }; class ControllerEmu { public: class ControlGroup { public: class Control { protected: Control( ControllerInterface::ControlReference* const _ref, const char * const _name ) : control_ref(_ref), name(_name){} public: virtual ~Control(); ControllerInterface::ControlReference* const control_ref; const char * const name; }; class Input : public Control { public: Input( const char * const _name ) : Control( new ControllerInterface::InputReference, _name ) {} }; class Output : public Control { public: Output( const char * const _name ) : Control( new ControllerInterface::OutputReference, _name ) {} }; class Setting { public: Setting(const char* const _name, const ControlState def_value , const unsigned int _low = 0, const unsigned int _high = 100 ) : name(_name) , value(def_value) , default_value(def_value) , low(_low) , high(_high){} const char* const name; ControlState value; const ControlState default_value; const unsigned int low, high; }; ControlGroup( const char* const _name, const unsigned int _type = GROUP_TYPE_OTHER ) : name(_name), type(_type) {} virtual ~ControlGroup(); virtual void LoadConfig(IniFile::Section *sec, const std::string& defdev = "", const std::string& base = "" ); virtual void SaveConfig(IniFile::Section *sec, const std::string& defdev = "", const std::string& base = "" ); const char* const name; const unsigned int type; std::vector< Control* > controls; std::vector< Setting* > settings; }; class AnalogStick : public ControlGroup { public: template void GetState( C* const x, C* const y, const unsigned int base, const unsigned int range ) { // this is all a mess ControlState yy = controls[0]->control_ref->State() - controls[1]->control_ref->State(); ControlState xx = controls[3]->control_ref->State() - controls[2]->control_ref->State(); ControlState deadzone = settings[0]->value; ControlState square = settings[1]->value; ControlState m = controls[4]->control_ref->State(); // modifier code if ( m ) { yy = (fabsf(yy)>deadzone) * sign(yy) * (m + deadzone/2); xx = (fabsf(xx)>deadzone) * sign(xx) * (m + deadzone/2); } // deadzone / square stick code if ( deadzone || square ) { // this section might be all wrong, but its working good enough, i think ControlState ang = atan2( yy, xx ); ControlState ang_sin = sin(ang); ControlState ang_cos = cos(ang); // the amt a full square stick would have at current angle ControlState square_full = std::min( ang_sin ? 1/fabsf(ang_sin) : 2, ang_cos ? 1/fabsf(ang_cos) : 2 ); // the amt a full stick would have that was ( user setting squareness) at current angle // i think this is more like a pointed circle rather than a rounded square like it should be ControlState stick_full = ( 1 + ( square_full - 1 ) * square ); ControlState dist = sqrt(xx*xx + yy*yy); // dead zone code dist = std::max( 0.0f, dist - deadzone * stick_full ); dist /= ( 1 - deadzone ); // square stick code ControlState amt = dist / stick_full; dist -= ((square_full - 1) * amt * square); yy = std::max( -1.0f, std::min( 1.0f, ang_sin * dist ) ); xx = std::max( -1.0f, std::min( 1.0f, ang_cos * dist ) ); } *y = C( yy * range + base ); *x = C( xx * range + base ); } AnalogStick( const char* const _name ); }; class Buttons : public ControlGroup { public: Buttons( const char* const _name ); template void GetState( C* const buttons, const C* bitmasks ) { std::vector::iterator i = controls.begin(), e = controls.end(); for ( ; i!=e; ++i, ++bitmasks ) if ( (*i)->control_ref->State() > settings[0]->value ) // threshold *buttons |= *bitmasks; } }; class MixedTriggers : public ControlGroup { public: template void GetState( C* const digital, const C* bitmasks, S* analog, const unsigned int range ) { const unsigned int trig_count = ((unsigned int) (controls.size() / 2)); for ( unsigned int i=0; icontrol_ref->State() > settings[0]->value ) //threshold { *analog = range; *digital |= *bitmasks; } else *analog = S(controls[i+trig_count]->control_ref->State() * range); } } MixedTriggers( const char* const _name ); }; class Triggers : public ControlGroup { public: template void GetState( S* analog, const unsigned int range ) { const unsigned int trig_count = ((unsigned int) (controls.size())); const ControlState deadzone = settings[0]->value; for ( unsigned int i=0; icontrol_ref->State() - deadzone, 0.0f) / (1 - deadzone) * range ); } Triggers( const char* const _name ); }; class Force : public ControlGroup { public: Force( const char* const _name ); template void GetState(C* axis, const u8 base, const R range) { const float deadzone = settings[0]->value; for (unsigned int i=0; i<6; i+=2) { float tmpf = 0; const float state = controls[i+1]->control_ref->State() - controls[i]->control_ref->State(); if (fabsf(state) > deadzone) tmpf = ((state - (deadzone * sign(state))) / (1 - deadzone)); else tmpf = 0; float &ax = m_swing[i >> 1]; if (fabs(tmpf) > fabsf(ax)) { if (tmpf > ax) ax = std::min(ax + 0.15f, tmpf); else if (tmpf < ax) ax = std::max(ax - 0.15f, tmpf); } else ax = tmpf; *axis++ = (C)(ax * range + base); } } private: float m_swing[3]; }; class Tilt : public ControlGroup { public: Tilt( const char* const _name ); template void GetState(C* const x, C* const y, const unsigned int base, const R range, const bool step = true) { // this is all a mess ControlState yy = controls[0]->control_ref->State() - controls[1]->control_ref->State(); ControlState xx = controls[3]->control_ref->State() - controls[2]->control_ref->State(); ControlState deadzone = settings[0]->value; ControlState circle = settings[1]->value; ControlState m = controls[4]->control_ref->State(); // modifier code if ( m ) { yy = (fabsf(yy)>deadzone) * sign(yy) * (m + deadzone/2); xx = (fabsf(xx)>deadzone) * sign(xx) * (m + deadzone/2); } // deadzone / circle stick code if ( deadzone || circle ) { // this section might be all wrong, but its working good enough, i think ControlState ang = atan2( yy, xx ); ControlState ang_sin = sin(ang); ControlState ang_cos = cos(ang); // the amt a full square stick would have at current angle ControlState square_full = std::min( ang_sin ? 1/fabsf(ang_sin) : 2, ang_cos ? 1/fabsf(ang_cos) : 2 ); // the amt a full stick would have that was ( user setting circular ) at current angle // i think this is more like a pointed circle rather than a rounded square like it should be ControlState stick_full = (square_full * (1 - circle)) + (circle); ControlState dist = sqrt(xx*xx + yy*yy); // dead zone code dist = std::max( 0.0f, dist - deadzone * stick_full ); dist /= (1 - deadzone); // circle stick code ControlState amt = dist / stick_full; dist += (square_full - 1) * amt * circle; yy = std::max( -1.0f, std::min( 1.0f, ang_sin * dist ) ); xx = std::max( -1.0f, std::min( 1.0f, ang_cos * dist ) ); } // this is kinda silly here // gui being open will make this happen 2x as fast, o well // silly if (step) { if (xx > m_tilt[0]) m_tilt[0] = std::min(m_tilt[0] + 0.1f, xx); else if (xx < m_tilt[0]) m_tilt[0] = std::max(m_tilt[0] - 0.1f, xx); if (yy > m_tilt[1]) m_tilt[1] = std::min(m_tilt[1] + 0.1f, yy); else if (yy < m_tilt[1]) m_tilt[1] = std::max(m_tilt[1] - 0.1f, yy); } *y = C( m_tilt[1] * range + base ); *x = C( m_tilt[0] * range + base ); } private: float m_tilt[2]; }; class Cursor : public ControlGroup { public: Cursor( const char* const _name, const SWiimoteInitialize* const _wiimote_initialize ); template void GetState( C* const x, C* const y, C* const z, const bool adjusted = false ) { const float zz = controls[4]->control_ref->State() - controls[5]->control_ref->State(); // silly being here if (zz > m_z) m_z = std::min(m_z + 0.1f, zz); else if (zz < m_z) m_z = std::max(m_z - 0.1f, zz); *z = m_z; // hide if (controls[6]->control_ref->State() > 0.5f) { *x = 10000; *y = 0; } else { float yy = controls[0]->control_ref->State() - controls[1]->control_ref->State(); float xx = controls[3]->control_ref->State() - controls[2]->control_ref->State(); // adjust cursor according to settings if (adjusted) { xx *= ( settings[1]->value * 2 ); yy *= ( settings[2]->value * 2 ); yy += ( settings[0]->value - 0.5f ); } *x = xx; *y = yy; } } private: const SWiimoteInitialize* const wiimote_initialize; float m_z; }; class Extension : public ControlGroup { public: Extension( const char* const _name ) : ControlGroup( _name, GROUP_TYPE_EXTENSION ) , switch_extension(0) , active_extension(0) {} ~Extension(); void GetState( u8* const data, const bool focus = true ); std::vector attachments; int switch_extension; int active_extension; }; virtual ~ControllerEmu(); virtual std::string GetName() const = 0; virtual void LoadDefaults(const ControllerInterface& ciface); virtual void LoadConfig(IniFile::Section *sec, const std::string& base = ""); virtual void SaveConfig(IniFile::Section *sec, const std::string& base = ""); void UpdateDefaultDevice(); void UpdateReferences( ControllerInterface& devi ); std::vector< ControlGroup* > groups; ControllerInterface::DeviceQualifier default_device; }; #endif