From: <sv...@ww...> - 2005-06-06 05:48:47
|
Author: mkrose Date: 2005-06-05 22:48:41 -0700 (Sun, 05 Jun 2005) New Revision: 1549 Modified: trunk/CSP/CSPSim/CHANGES.current trunk/CSP/CSPSim/Include/InputInterface.h trunk/CSP/CSPSim/Source/InputInterface.cpp Log: Add support for instance-specific event dispatch handlers. Previously all handlers were static class properties. The new handlers are implemented using sigc callbacks. The initial use case is for cockpit elements that auto-declare multiple event handlers. For example, a 3-state switch can declare events to set each state directly, as well as cycle forward and cycle backward. If the event prefix was named MASTER_ARM, the generated events might be MASTER_ARM_OFF, MASTER_ARM_SIM, MASTER_ARM_ON, MASTER_ARM_CYCLE_NEXT, MASTER_ARM_CYCLE_PREV. This avoids having to manually declare five events and the corresponding handlers, and automatically adjusts when new states are added. Browse at: https://www.zerobar.net/viewcvs/viewcvs.cgi?view=rev&rev=1549 Modified: trunk/CSP/CSPSim/CHANGES.current =================================================================== --- trunk/CSP/CSPSim/CHANGES.current 2005-06-06 05:36:05 UTC (rev 1548) +++ trunk/CSP/CSPSim/CHANGES.current 2005-06-06 05:48:41 UTC (rev 1549) @@ -7,6 +7,19 @@ rendering, which is helpful when running multiple instances on the same machine to test networking. + * Add support for instance-specific event dispatch handlers. + Previously all handlers were static class properties. The + new handlers are implemented using sigc callbacks. The initial + use case is for cockpit elements that auto-declare multiple + event handlers. For example, a 3-state switch can declare events + to set each state directly, as well as cycle forward and cycle + backward. If the event prefix was named MASTER_ARM, the + generated events might be MASTER_ARM_OFF, MASTER_ARM_SIM, + MASTER_ARM_ON, MASTER_ARM_CYCLE_NEXT, MASTER_ARM_CYCLE_PREV. + This avoids having to manually declare five events and the + corresponding handlers, and automatically adjusts when new + states are added. + 2005-05-14: onsight * Add a list of contacts to SimObject of nearby vehicles that is maintained by the Battlefield. The battlefield was already Modified: trunk/CSP/CSPSim/Include/InputInterface.h =================================================================== --- trunk/CSP/CSPSim/Include/InputInterface.h 2005-06-06 05:36:05 UTC (rev 1548) +++ trunk/CSP/CSPSim/Include/InputInterface.h 2005-06-06 05:48:41 UTC (rev 1549) @@ -37,8 +37,10 @@ #ifndef __INPUTINTERFACE_H__ #define __INPUTINTERFACE_H__ +#include <map> #include <string> #include <cassert> +#include <sigc++/sigc++.h> #include <SDL/SDL_events.h> #include <SimData/HashUtility.h> @@ -50,6 +52,14 @@ class InputInterface; +typedef SigC::Slot2<void, int, int> ActionEventSlot; +typedef SigC::Signal2<void, int, int> ActionEventSignal; +typedef SigC::Slot4<void, int, int, int, int> MotionEventSlot; +typedef SigC::Signal4<void, int, int, int, int> MotionEventSignal; +typedef SigC::Slot1<void, double> AxisEventSlot; +typedef SigC::Signal1<void, double> AxisEventSignal; + + /** Abstract adapter for action event handlers (internal) */ struct ActionAdapter { @@ -139,8 +149,33 @@ }; +/** A dispatch mapping that is instance specific and constructed at runtime (as + * opposed to InputInterfaceDispatch which is a static class property). This + * dispatcher uses arbitrary sigc callbacks, so the handlers do not have to be + * InputInterface subclass methods. + */ +class RuntimeDispatch { +public: + bool onMapEvent(MapEvent const &); + void bindAction(std::string const &id, ActionEventSlot const &callback); + void bindMotion(std::string const &id, MotionEventSlot const &callback); + void bindAxis(std::string const &id, AxisEventSlot const &callback); +private: + typedef std::map<std::string, ActionEventSignal> ActionCallbacks; + typedef std::map<std::string, MotionEventSignal> MotionCallbacks; + typedef std::map<std::string, AxisEventSignal> AxisCallbacks; + ActionCallbacks m_ActionCallbacks; + MotionCallbacks m_MotionCallbacks; + AxisCallbacks m_AxisCallbacks; +}; + + /** Mappings for dispatching input events to handlers defined in InputInterface - * subclasses. (internal) + * subclasses. (internal) This mapping is class static, such that it is + * defined at compile time for a class using the DECLARE_INPUT_INTERFACE + * macros below. Static dispatch maps conserve space since they are shared + * by all instances of an InputInterface subclass. InputInterface instances + * may also have dynamic dispatch maps if needed. */ class InputInterfaceDispatch { private: @@ -251,6 +286,14 @@ /** Interface for classes that can handle input events. * + * InputInterface subclasses can define class-specific handlers using the + * various BIND macros. These bindings map events to methods within the + * InputInterface subclass, and do not incur any per-instance runtime + * storage costs. InputInterface also supports dynamic event handlers + * that are created at runtime. There is a per-instance storage cost for + * dynamic handlers. Dynamic handlers override static handlers for the + * same event id. + * * The following is a minimal example of a class that defines handlers * for a few events. More elaborate behavior can be achieved by overriding * some of the virtual methods in this interface. The definition of the @@ -307,7 +350,8 @@ */ class InputInterface { public: - virtual ~InputInterface() {} + InputInterface(); + virtual ~InputInterface(); /** These methods are called by VirtualHID to allow the InputInterface to access * the raw input events. The default implementation returns false, which causes @@ -329,6 +373,29 @@ */ virtual bool onMapEvent(MapEvent const &); + /** Bind a handler to an action event at runtime. Using runtime + * handlers (as opposed to the class handlers set by the BIND + * macros) incurs storage costs per instance. + */ + void bindActionEvent(std::string const &id, ActionEventSlot const &slot) { + runtimeDispatch()->bindAction(id, slot); + } + + /** Bind a handler to an axis event at runtime. Using runtime + * handlers (as opposed to the class handlers set by the BIND + * macros) incurs storage costs per instance. + */ + void bindAxisEvent(std::string const &id, AxisEventSlot const &slot) { + runtimeDispatch()->bindAxis(id, slot); + } + + /** Bind a handler to a motion event at runtime. Using runtime + * handlers (as opposed to the class handlers set by the BIND + * macros) incurs storage costs per instance. + */ + void bindMotionEvent(std::string const &id, MotionEventSlot const &slot) { + runtimeDispatch()->bindMotion(id, slot); + } protected: /** Internal */ @@ -341,6 +408,12 @@ bool onCommand(std::string const &id, int x, int y); bool onAxis(std::string const &id, double value); bool onMotion(std::string const &id, int x, int y, int dx, int dy); + + RuntimeDispatch *runtimeDispatch() { + if (!m_RuntimeDispatch) m_RuntimeDispatch = new RuntimeDispatch; + return m_RuntimeDispatch; + } + RuntimeDispatch *m_RuntimeDispatch; }; Modified: trunk/CSP/CSPSim/Source/InputInterface.cpp =================================================================== --- trunk/CSP/CSPSim/Source/InputInterface.cpp 2005-06-06 05:36:05 UTC (rev 1548) +++ trunk/CSP/CSPSim/Source/InputInterface.cpp 2005-06-06 05:48:41 UTC (rev 1549) @@ -40,6 +40,12 @@ } } +InputInterface::InputInterface(): m_RuntimeDispatch(0) { } + +InputInterface::~InputInterface() { + delete m_RuntimeDispatch; // ok if null +} + bool InputInterface::onCommand(std::string const &id, int x, int y) { InputInterfaceDispatch *map = _getInputInterfaceDispatch(); if (!map) return false; @@ -59,6 +65,9 @@ } bool InputInterface::onMapEvent(MapEvent const &event) { + if (m_RuntimeDispatch) { + return m_RuntimeDispatch->onMapEvent(event); + } switch (event.type) { case MapEvent::ID_COMMAND_EVENT: return onCommand(event.id, event.id_command.x, event.id_command.y); @@ -72,3 +81,41 @@ return false; } +bool RuntimeDispatch::onMapEvent(MapEvent const &event) { + ActionCallbacks::iterator action_iter; + MotionCallbacks::iterator motion_iter; + AxisCallbacks::iterator axis_iter; + switch (event.type) { + case MapEvent::ID_COMMAND_EVENT: + action_iter = m_ActionCallbacks.find(event.id); + if (action_iter == m_ActionCallbacks.end()) return false; + action_iter->second.emit(event.id_command.x, event.id_command.y); + return true; + case MapEvent::ID_AXIS_EVENT: + axis_iter = m_AxisCallbacks.find(event.id); + if (axis_iter == m_AxisCallbacks.end()) return false; + axis_iter->second.emit(event.id_axis.value); + return true; + case MapEvent::ID_MOTION_EVENT: + motion_iter = m_MotionCallbacks.find(event.id); + if (motion_iter == m_MotionCallbacks.end()) return false; + motion_iter->second.emit(event.id_motion.x, event.id_motion.y, event.id_motion.dx, event.id_motion.dy); + return true; + default: + break; + } + return false; +}; + +void RuntimeDispatch::bindAction(std::string const &id, ActionEventSlot const &callback) { + m_ActionCallbacks[id].connect(callback); +} + +void RuntimeDispatch::bindMotion(std::string const &id, MotionEventSlot const &callback) { + m_MotionCallbacks[id].connect(callback); +} + +void RuntimeDispatch::bindAxis(std::string const &id, AxisEventSlot const &callback) { + m_AxisCallbacks[id].connect(callback); +} + |