namespace Input {
BinaryControl::BinaryControl():
- state(false)
+ state(false),
+ threshold(0.5)
{ }
BinaryControl::BinaryControl(const ControlSource &s):
Control(s),
- state(false)
+ state(false),
+ threshold(0.5)
{ }
BinaryControl::BinaryControl(Device &d, ControlSrcType t, unsigned i):
Control(d, t, i),
- state(false)
+ state(false),
+ threshold(0.5)
{ }
+void BinaryControl::set_threshold(float t)
+{
+ threshold=t;
+}
+
void BinaryControl::on_press()
{
if(!state)
void BinaryControl::on_motion(float value, float)
{
- if(value>src.dev->get_axis_threshold())
+ if(value>threshold)
on_press();
else
on_release();
/**
A control with two possible states. Button state is mapped directly. An axis
-is considered to be active when its value is above a threshold (defined by the
-input device).
+is considered to be active when its value is within 10% of the end of the axis.
*/
class BinaryControl: public Control
{
private:
bool state;
+ float threshold;
public:
BinaryControl();
BinaryControl(const ControlSource &);
BinaryControl(Device &, ControlSrcType, unsigned);
+
+ /**
+ Sets the threshold between states for axis sources. No effect on button
+ sources
+ */
+ void set_threshold(float);
+
bool get_state() const { return state; }
private:
{
if(type==BUTTON)
return dev->get_button_name(index);
- else if(type==AXIS_POS || type==AXIS_NEG)
- return dev->get_axis_name(index);
+ else if(type==AXIS_POS)
+ return dev->get_axis_name(index)+" +";
+ else if(type==AXIS_NEG)
+ return dev->get_axis_name(index)+" -";
else if(type==NONE)
return "None";
connect_signals();
}
-Control::Control(const Control &c):
- trackable(c),
- src(c.src),
- capture_dev(0)
-{
- connect_signals();
-}
-
-Control &Control::operator=(const Control &c)
-{
- notify_callbacks();
- src=c.src;
- capture_dev=0;
- connect_signals();
-
- return *this;
-}
-
void Control::capture(Device &d)
{
notify_callbacks();
if(capture_dev)
{
ControlSrcType type=NONE;
- if(v<-src.dev->get_axis_threshold())
+ if(v<-0.9)
type=AXIS_NEG;
- else if(v>src.dev->get_axis_threshold())
+ else if(v>0.9)
type=AXIS_POS;
if(type!=NONE)
Control();
Control(const ControlSource &);
Control(Device &, ControlSrcType, unsigned);
- Control(const Control &);
public:
- Control &operator=(const Control &);
virtual ~Control() { }
void capture(Device &);
void button_press(unsigned);
void button_release(unsigned);
void axis_motion(unsigned, float, float);
+
+ Control(const Control &);
+ Control &operator=(const Control &);
};
} // namespace Input
/**
Base class for input devices. Input devices have two types of controls:
-buttons and axes. Buttons are either on or off. Axes have a floating-point
-value, with range depending on the device.
+buttons and axes. Buttons are either on or off. Axes have a floating point
+value in the range [-1, 1].
*/
class Device
{
std::string name;
std::vector<char> buttons;
std::vector<float> axes;
- float axis_threshold;
- float axis_dead_zone;
Device() { }
public:
const std::string &get_name() const { return name; }
bool get_button_state(unsigned) const;
float get_axis_value(unsigned) const;
- float get_axis_threshold() const { return axis_threshold; }
virtual std::string get_button_name(unsigned) const;
virtual std::string get_axis_name(unsigned) const;
std::string Hub::get_button_name(unsigned btn) const
{
unsigned dev_index=btn>>12;
- if(dev_index>devices.size())
+ if(dev_index>=devices.size())
throw InvalidParameterValue("Button does not exist");
const Device &dev=*devices[dev_index];
return dev.get_name()+": "+dev.get_button_name(btn&0xFFF);
}
+std::string Hub::get_axis_name(unsigned axis) const
+{
+ unsigned dev_index=axis>>12;
+ if(dev_index>=devices.size())
+ throw InvalidParameterValue("Axis does not exist");
+
+ const Device &dev=*devices[dev_index];
+ return dev.get_name()+": "+dev.get_axis_name(axis&0xFFF);
+}
+
void Hub::button_press(unsigned btn, unsigned index)
{
set_button_state((index<<12) | (btn&0xFFF), true, true);
unsigned attach(Device &dev);
virtual std::string get_button_name(unsigned) const;
+ virtual std::string get_axis_name(unsigned) const;
protected:
void button_press(unsigned, unsigned);
void button_release(unsigned, unsigned);
namespace Input {
Mouse::Mouse(Graphics::Window &w):
- window(w),
- axis_scale(0.01)
+ window(w)
{
name="Mouse";
}
}
+std::string Mouse::get_axis_name(unsigned axis) const
+{
+ switch(axis)
+ {
+ case 0:
+ return "X axis";
+ case 1:
+ return "Y axis";
+ default:
+ return format("Axis %d", axis);
+ };
+}
+
void Mouse::button_press(int, int, unsigned btn, unsigned)
{
set_button_state(btn, true, true);
void Mouse::pointer_motion(int x, int y)
{
- set_axis_value(0, x*axis_scale, true);
- set_axis_value(1, y*axis_scale, true);
+ set_axis_value(0, x*2.0f/window.get_width()-1.0f, true);
+ set_axis_value(1, 1.0f-y*2.0f/window.get_height(), true);
}
} // namespace Input
namespace Msp {
namespace Input {
+/**
+Mouse device. Receives events from a Graphics::Window and presents them in a
+uniform way.
+
+Note: Y axis grows upwards.
+*/
class Mouse: public Device
{
private:
Graphics::Window &window;
- float axis_scale;
public:
Mouse(Graphics::Window &);
virtual std::string get_button_name(unsigned) const;
+ virtual std::string get_axis_name(unsigned) const;
private:
void button_press(int, int, unsigned, unsigned);
void button_release(int, int, unsigned, unsigned);
SmoothControl::SmoothControl():
value(0),
- paired_ctrl(0)
+ paired_ctrl(0),
+ dead_zone(0.1),
+ threshold(0.9)
{ }
SmoothControl::SmoothControl(const ControlSource &s):
Control(s),
value(0),
- paired_ctrl(0)
+ paired_ctrl(0),
+ dead_zone(0.1),
+ threshold(0.9)
{ }
SmoothControl::SmoothControl(Device &d, ControlSrcType t, unsigned i):
Control(d, t, i),
value(0),
- paired_ctrl(0)
+ paired_ctrl(0),
+ dead_zone(0.1),
+ threshold(0.9)
{ }
-SmoothControl &SmoothControl::operator=(const SmoothControl &sc)
+SmoothControl::~SmoothControl()
{
- Control::operator=(sc);
+ pair(0);
+}
- return *this;
+void SmoothControl::set_dead_zone(float d)
+{
+ dead_zone=d;
}
-SmoothControl::~SmoothControl()
+void SmoothControl::set_threshold(float t)
{
- pair(0);
+ threshold=t;
}
void SmoothControl::pair(SmoothControl *ctrl)
on_motion(0, -value);
}
-void SmoothControl::on_motion(float v, float)
+void SmoothControl::on_motion(float v, float r)
{
- value=v;
+ if(v<-threshold)
+ value=-1;
+ else if(v>threshold)
+ value=1;
+ else if(v<-dead_zone)
+ value=(v+dead_zone)/(threshold-dead_zone);
+ else if(v>dead_zone)
+ value=(v-dead_zone)/(threshold-dead_zone);
+ else
+ value=0;
+
signal_motion.emit(value);
-
- if(paired_ctrl && paired_ctrl->get_value()!=-value)
- paired_ctrl->on_motion(-value, -value-paired_ctrl->get_value());
+
+ if(paired_ctrl && (v>0 || (v==0 && paired_ctrl->value!=0)))
+ paired_ctrl->on_motion(-v, -r);
}
} // namespace Input
Two smooth controls can also be paired to each other. Motion on one control of
the pair will cause negative motion on the other. This works best when the
-controls are bound to the opposite sides of the same joystick axis.
+controls are bound to the opposite sides of the same axis.
*/
class SmoothControl: public Control
{
private:
float value;
SmoothControl *paired_ctrl;
+ float dead_zone;
+ float threshold;
public:
SmoothControl();
SmoothControl(const ControlSource &);
SmoothControl(Device &, ControlSrcType, unsigned);
- SmoothControl &operator=(const SmoothControl &);
virtual ~SmoothControl();
+ /// Sets the dead zone value. Any value below this will be treated as 0.
+ void set_dead_zone(float);
+
+ /// Sets the max-out threshold. Any value above this will be treated as 1.
+ void set_threshold(float);
+
void pair(SmoothControl *ctrl);
float get_value() const { return value; }