--- /dev/null
+/* $Id$
+
+This file is part of libmspgbase
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#include "binarycontrol.h"
+#include "inputdevice.h"
+
+namespace Msp {
+namespace Input {
+
+BinaryControl::BinaryControl():
+ state(false)
+{ }
+
+BinaryControl::BinaryControl(const ControlSource &s):
+ Control(s),
+ state(false)
+{ }
+
+BinaryControl::BinaryControl(Device &d, ControlSrcType t, unsigned i):
+ Control(d, t, i),
+ state(false)
+{ }
+
+void BinaryControl::on_press()
+{
+ if(!state)
+ {
+ state=true;
+ signal_press.emit();
+ }
+}
+
+void BinaryControl::on_release()
+{
+ if(state)
+ {
+ state=false;
+ signal_release.emit();
+ }
+}
+
+void BinaryControl::on_motion(float value, float)
+{
+ if(value>src.dev->get_axis_threshold())
+ on_press();
+ else
+ on_release();
+}
+
+} // namespace Input
+} // namespace Msp
--- /dev/null
+/* $Id$
+
+This file is part of libmspgbase
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_GBASE_BINARYCONTROL_H_
+#define MSP_GBASE_BINARYCONTROL_H_
+
+#include "control.h"
+
+namespace Msp {
+namespace Input {
+
+/**
+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).
+*/
+class BinaryControl: public Control
+{
+public:
+ sigc::signal<void> signal_press;
+ sigc::signal<void> signal_release;
+
+private:
+ bool state;
+
+public:
+ BinaryControl();
+ BinaryControl(const ControlSource &);
+ BinaryControl(Device &, ControlSrcType, unsigned);
+ bool get_state() const { return state; }
+
+private:
+ virtual void on_press();
+ virtual void on_release();
+ virtual void on_motion(float, float);
+};
+
+} // namespace Input
+} // namespace Msp
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of libmspgbase
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#include <msp/core/error.h>
+#include "control.h"
+#include "inputdevice.h"
+
+namespace Msp {
+namespace Input {
+
+ControlSource::ControlSource():
+ dev(0),
+ type(NONE),
+ index(0)
+{ }
+
+ControlSource::ControlSource(Device &d, ControlSrcType t, unsigned i):
+ dev(&d),
+ type(t),
+ index(i)
+{ }
+
+
+Control::Control():
+ capture_dev(0)
+{ }
+
+Control::Control(const ControlSource &s):
+ src(s),
+ capture_dev(0)
+{ }
+
+Control::Control(Device &d, ControlSrcType t, unsigned i):
+ src(d, t, i),
+ capture_dev(0)
+{
+ 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();
+ capture_dev=&d;
+ capture_dev->signal_button_press.connect(sigc::mem_fun(this, &Control::button_press));
+ capture_dev->signal_axis_motion.connect(sigc::mem_fun(this, &Control::axis_motion));
+}
+
+void Control::cancel_capture()
+{
+ notify_callbacks();
+ capture_dev=0;
+ connect_signals();
+}
+
+void Control::connect_signals()
+{
+ switch(src.type)
+ {
+ case NONE:
+ break;
+ case BUTTON:
+ src.dev->signal_button_press.connect(sigc::mem_fun(this, &Control::button_press));
+ src.dev->signal_button_release.connect(sigc::mem_fun(this, &Control::button_release));
+ break;
+ case AXIS_POS:
+ case AXIS_NEG:
+ src.dev->signal_axis_motion.connect(sigc::mem_fun(this, &Control::axis_motion));
+ break;
+ default:
+ throw Exception("Invalid source in Control");
+ }
+}
+
+void Control::button_press(unsigned i)
+{
+ if(capture_dev)
+ {
+ src.dev=capture_dev;
+ src.type=BUTTON;
+ src.index=i;
+
+ notify_callbacks();
+ capture_dev=0;
+ connect_signals();
+ capture_complete.emit();
+ }
+ else if(src.type==BUTTON && i==src.index)
+ on_press();
+}
+
+void Control::button_release(unsigned i)
+{
+ if(src.type==BUTTON && i==src.index)
+ on_release();
+}
+
+void Control::axis_motion(unsigned i, float v, float r)
+{
+ if(capture_dev)
+ {
+ ControlSrcType type=NONE;
+ if(v<-src.dev->get_axis_threshold())
+ type=AXIS_NEG;
+ else if(v>src.dev->get_axis_threshold())
+ type=AXIS_POS;
+
+ if(type!=NONE)
+ {
+ src.dev=capture_dev;
+ src.type=type;
+ src.index=i;
+ notify_callbacks();
+ connect_signals();
+ capture_complete.emit();
+ }
+ }
+ else if(src.type==AXIS_POS && i==src.index && v>=0)
+ on_motion(v, r);
+ else if(src.type==AXIS_NEG && i==src.index && v<=0)
+ on_motion(-v, -r);
+}
+
+} // namespace Input
+} // namespace Msp
--- /dev/null
+/* $Id$
+
+This file is part of libmspgbase
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_GBASE_CONTROL_H_
+#define MSP_GBASE_CONTROL_H_
+
+#include <sigc++/signal.h>
+#include <sigc++/trackable.h>
+
+namespace Msp {
+namespace Input {
+
+class Device;
+
+enum ControlSrcType
+{
+ NONE,
+ BUTTON,
+ AXIS_POS,
+ AXIS_NEG
+};
+
+/**
+Specifies the source of a control. This provides a way for setting sources for
+different types of controls in a uniform way.
+*/
+struct ControlSource
+{
+ Device *dev;
+ ControlSrcType type;
+ unsigned index;
+
+ ControlSource();
+ ControlSource(Device &, ControlSrcType, unsigned);
+};
+
+/**
+Provides further abstraction on top of input devices. There are two types of
+controls currently defined: BinaryControl and SmoothControl.
+
+A control uses either a button or half of an axis (positive or negative) as its
+source. How the source values are interpreted depends on the exact type of the
+control. Controls also support interactive binding by capturing a button press
+or axis motion.
+*/
+class Control: public sigc::trackable
+{
+public:
+ sigc::signal<void> capture_complete;
+
+protected:
+ ControlSource src;
+ Device *capture_dev;
+
+ Control();
+ Control(const ControlSource &);
+ Control(Device &, ControlSrcType, unsigned);
+ Control(const Control &);
+public:
+ Control &operator=(const Control &);
+ virtual ~Control() { }
+
+ void capture(Device &);
+ void cancel_capture();
+ const ControlSource &get_source() const { return src; }
+protected:
+ virtual void on_press() =0;
+ virtual void on_release() =0;
+ virtual void on_motion(float, float) =0;
+
+private:
+ void connect_signals();
+ void button_press(unsigned);
+ void button_release(unsigned);
+ void axis_motion(unsigned, float, float);
+};
+
+} // namespace Input
+} // namespace Msp
+
+#endif
namespace Msp {
namespace Input {
-bool Device::get_button_state(unsigned btn)
+bool Device::get_button_state(unsigned btn) const
{
if(btn>buttons.size())
return false;
return buttons[btn];
}
-float Device::get_axis_value(unsigned axis)
+float Device::get_axis_value(unsigned axis) const
{
if(axis>axes.size())
return 0;
protected:
std::vector<char> buttons;
std::vector<float> axes;
+ float axis_threshold;
+ float axis_dead_zone;
+ Device() { }
public:
virtual ~Device() { }
- bool get_button_state(unsigned);
- float get_axis_value(unsigned);
+ bool get_button_state(unsigned) const;
+ float get_axis_value(unsigned) const;
+ float get_axis_threshold() const { return axis_threshold; }
protected:
void set_button_state(unsigned, bool, bool);
void set_axis_value(unsigned, float, bool);
+/* $Id$
+
+This file is part of libmspgbase
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
#include <sigc++/bind.h>
#include "inputhub.h"
--- /dev/null
+/* $Id$
+
+This file is part of libmspgbase
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#include "smoothcontrol.h"
+
+namespace Msp {
+namespace Input {
+
+SmoothControl::SmoothControl():
+ value(0),
+ paired_ctrl(0)
+{ }
+
+SmoothControl::SmoothControl(const ControlSource &s):
+ Control(s),
+ value(0),
+ paired_ctrl(0)
+{ }
+
+SmoothControl::SmoothControl(Device &d, ControlSrcType t, unsigned i):
+ Control(d, t, i),
+ value(0),
+ paired_ctrl(0)
+{ }
+
+void SmoothControl::pair(SmoothControl *ctrl)
+{
+ if(ctrl==paired_ctrl)
+ return;
+
+ if(paired_ctrl)
+ {
+ paired_ctrl=0;
+ paired_ctrl->pair(0);
+ }
+
+ paired_ctrl=ctrl;
+
+ if(paired_ctrl)
+ paired_ctrl->pair(this);
+}
+
+void SmoothControl::on_press()
+{
+ on_motion(1, 1-value);
+}
+
+void SmoothControl::on_release()
+{
+ on_motion(0, -value);
+}
+
+void SmoothControl::on_motion(float v, float)
+{
+ value=v;
+ signal_motion.emit(value);
+
+ if(paired_ctrl)
+ paired_ctrl->signal_motion.emit(-value);
+}
+
+} // namespace Input
+} // namespace Msp
--- /dev/null
+/* $Id$
+
+This file is part of libmspgbase
+Copyright © 2007 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+
+#ifndef MSP_GBASE_SMOOTHCONTROL_H_
+#define MSP_GBASE_SMOOTHCONTROL_H_
+
+#include "control.h"
+
+namespace Msp {
+namespace Input {
+
+/**
+A control with a continuous range of values. A button is treated as either 1
+or 0. Axis values are mapped to absolute value.
+
+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.
+*/
+class SmoothControl: public Control
+{
+public:
+ sigc::signal<void, float> signal_motion;
+
+private:
+ float value;
+ SmoothControl *paired_ctrl;
+
+public:
+ SmoothControl();
+ SmoothControl(const ControlSource &);
+ SmoothControl(Device &, ControlSrcType, unsigned);
+
+ void pair(SmoothControl *ctrl);
+ float get_value() const { return value; }
+
+private:
+ virtual void on_press();
+ virtual void on_release();
+ virtual void on_motion(float, float);
+};
+
+} // namespace Input
+} // namespace Msp
+
+#endif