]> git.tdb.fi Git - libs/gui.git/commitdiff
Add classes for storing and applying bindings to controls
authorMikko Rasa <tdb@tdb.fi>
Sat, 19 Nov 2022 22:05:12 +0000 (00:05 +0200)
committerMikko Rasa <tdb@tdb.fi>
Sat, 19 Nov 2022 22:05:12 +0000 (00:05 +0200)
source/input/bindings.cpp [new file with mode: 0644]
source/input/bindings.h [new file with mode: 0644]
source/input/controlscheme.cpp [new file with mode: 0644]
source/input/controlscheme.h [new file with mode: 0644]

diff --git a/source/input/bindings.cpp b/source/input/bindings.cpp
new file mode 100644 (file)
index 0000000..85b5941
--- /dev/null
@@ -0,0 +1,142 @@
+#include <msp/strings/format.h>
+#include "bindings.h"
+#include "controlscheme.h"
+
+using namespace std;
+
+namespace Msp {
+namespace Input {
+
+vector<Device *> Bindings::resolve_devices(Device &dev) const
+{
+       vector<Device *> resolved_devs;
+       resolved_devs.reserve(devices.size());
+       for(vector<DeviceRef>::const_iterator i=devices.begin(); i!=devices.end(); ++i)
+       {
+               if(i->type!=UNSPECIFIED)
+                       resolved_devs.push_back(dev.find_subdevice(i->type));
+               else if(!i->name.empty())
+                       resolved_devs.push_back(dev.find_subdevice(i->name));
+               else
+                       resolved_devs.push_back(0);
+       }
+       return resolved_devs;
+}
+
+bool Bindings::is_compatible(Device &dev) const
+{
+       vector<Device *> resolved_devs = resolve_devices(dev);
+       for(vector<Device *>::const_iterator i=resolved_devs.begin(); i!=resolved_devs.end(); ++i)
+               if(!*i)
+                       return false;
+       return true;
+}
+
+bool Bindings::apply_to(ControlScheme &control_scheme, Device &dev)
+{
+       vector<Device *> resolved_devs = resolve_devices(dev);
+       bool applied = false;
+       for(vector<Binding>::const_iterator i=bindings.begin(); i!=bindings.end(); ++i)
+       {
+               Control *ctrl = control_scheme.find(i->control);
+               Device *bdev = (i->device<resolved_devs.size() ? resolved_devs[i->device] : 0);
+               if(ctrl && bdev)
+               {
+                       ctrl->set_source(*bdev, i->type, i->index);
+                       applied = true;
+               }
+       }
+
+       return applied;
+}
+
+
+DataFile::Loader::ActionMap Bindings::Loader::shared_actions;
+
+Bindings::Loader::Loader(Bindings &b):
+       ObjectLoader<Bindings>(b)
+{
+       set_actions(shared_actions);
+}
+
+void Bindings::Loader::init_actions()
+{
+       add("binding", &Loader::binding);
+       add("device", &Loader::device_type);
+       add("device", &Loader::device_name);
+}
+
+void Bindings::Loader::binding(const string &c)
+{
+       Binding bind;
+       bind.control = c;
+       load_sub(bind);
+       obj.bindings.push_back(bind);
+}
+
+void Bindings::Loader::device_type(DeviceType t)
+{
+       DeviceRef dev;
+       dev.type = t;
+       obj.devices.push_back(dev);
+}
+
+void Bindings::Loader::device_name(const string &n)
+{
+       DeviceRef dev;
+       dev.name = n;
+       obj.devices.push_back(dev);
+}
+
+
+DataFile::Loader::ActionMap Bindings::Binding::Loader::shared_actions;
+
+Bindings::Binding::Loader::Loader(Binding &b):
+       ObjectLoader<Binding>(b)
+{
+       set_actions(shared_actions);
+}
+
+void Bindings::Binding::Loader::init_actions()
+{
+       add("axis", &Loader::axis);
+       add("button", &Loader::button);
+       add("device", &Binding::device);
+       add("key", &Loader::key);
+}
+
+void Bindings::Binding::Loader::axis(unsigned a, AxisSide s)
+{
+       obj.type = (s==NEGATIVE ? AXIS_NEG : AXIS_POS);
+       obj.index = a;
+}
+
+void Bindings::Binding::Loader::button(unsigned b)
+{
+       obj.type = BUTTON;
+       obj.index = b;
+}
+
+
+void operator>>(const LexicalConverter &conv, Bindings::AxisSide &side)
+{
+       if(conv.get()=="POSITIVE")
+               side = Bindings::POSITIVE;
+       else if(conv.get()=="NEGATIVE")
+               side = Bindings::NEGATIVE;
+       else
+               throw lexical_error(format("conversion of '%s' to AxisSide", conv.get()));
+}
+
+void operator<<(LexicalConverter &conv, Bindings::AxisSide side)
+{
+       switch(side)
+       {
+       case Bindings::POSITIVE: conv.result("POSITIVE"); break;
+       case Bindings::NEGATIVE: conv.result("NEGATIVE"); break;
+       default: conv.result(format("AxisSide(%#x)", static_cast<int>(side)));
+       }
+}
+
+} // namespace Input
+} // namespace Msp;
diff --git a/source/input/bindings.h b/source/input/bindings.h
new file mode 100644 (file)
index 0000000..b73dc51
--- /dev/null
@@ -0,0 +1,91 @@
+#ifndef MSP_INPUT_BINDINGS_H_
+#define MSP_INPUT_BINDINGS_H_
+
+#include <msp/datafile/objectloader.h>
+#include <msp/strings/lexicalcast.h>
+#include "control.h"
+#include "device.h"
+#include "keys.h"
+
+namespace Msp {
+namespace Input {
+
+class ControlScheme;
+
+class Bindings
+{
+public:
+       class Loader: public DataFile::ObjectLoader<Bindings>
+       {
+       private:
+               static ActionMap shared_actions;
+
+       public:
+               Loader(Bindings &);
+
+       private:
+               virtual void init_actions();
+
+               void binding(const std::string &);
+               void device_type(DeviceType);
+               void device_name(const std::string &);
+       };
+
+       struct DeviceRef
+       {
+               DeviceType type = UNSPECIFIED;
+               std::string name;
+       };
+
+private:
+       enum AxisSide
+       {
+               POSITIVE,
+               NEGATIVE
+       };
+
+       friend void operator>>(const LexicalConverter &, AxisSide &);
+       friend void operator<<(LexicalConverter &, AxisSide);
+
+public:
+       struct Binding
+       {
+               class Loader: public DataFile::ObjectLoader<Binding>
+               {
+               private:
+                       static ActionMap shared_actions;
+
+               public:
+                       Loader(Binding &);
+
+               private:
+                       virtual void init_actions();
+
+                       void axis(unsigned, AxisSide);
+                       void button(unsigned);
+                       void key(Key k) { button(k); }
+               };
+
+               std::string control;
+               unsigned device;
+               ControlSrcType type;
+               unsigned index;
+       };
+
+private:
+       std::vector<DeviceRef> devices;
+       std::vector<Binding> bindings;
+
+public:
+       const std::vector<DeviceRef> &get_devices() const { return devices; }
+       std::vector<Device *> resolve_devices(Device &) const;
+       bool is_compatible(Device &) const;
+       const std::vector<Binding> &get_bindings() const { return bindings; }
+
+       bool apply_to(ControlScheme &, Device &);
+};
+
+} // namespace Input
+} // namespace Msp
+
+#endif
diff --git a/source/input/controlscheme.cpp b/source/input/controlscheme.cpp
new file mode 100644 (file)
index 0000000..fe1c0eb
--- /dev/null
@@ -0,0 +1,33 @@
+#include <msp/core/maputils.h>
+#include "control.h"
+#include "controlscheme.h"
+
+using namespace std;
+
+namespace Msp {
+namespace Input {
+
+void ControlScheme::add(const string &name, Control &ctrl)
+{
+       insert_unique(controls, name, &ctrl);
+}
+
+Control &ControlScheme::get(const string &name) const
+{
+       return *get_item(controls, name);
+}
+
+Control *ControlScheme::find(const string &name) const
+{
+       map<string, Control *>::const_iterator i = controls.find(name);
+       return (i!=controls.end() ? i->second : 0);
+}
+
+void ControlScheme::reset_edges()
+{
+       for(map<string, Control *>::const_iterator i=controls.begin(); i!=controls.end(); ++i)
+               i->second->reset_edges();
+}
+
+} // namespace Input
+} // namespace Msp
diff --git a/source/input/controlscheme.h b/source/input/controlscheme.h
new file mode 100644 (file)
index 0000000..dabcec2
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef MSP_INPUT_CONTROLSCHEME_H_
+#define MSP_INPUT_CONTROLSCHEME_H_
+
+#include <map>
+#include <string>
+
+namespace Msp {
+namespace Input {
+
+class Control;
+
+class ControlScheme
+{
+private:
+       std::map<std::string, Control *> controls;
+
+protected:
+       ControlScheme() = default;
+
+       void add(const std::string &, Control &);
+public:
+       Control &get(const std::string &) const;
+       Control *find(const std::string &) const;
+       const std::map<std::string, Control *> &get_controls() const { return controls; }
+
+       void reset_edges();
+};
+
+} // namespace Input
+} // namespace Msp
+
+#endif