forked from mirror/openmw-tes3mp
Created event dispatcher and function binder (for input system)
parent
d5228c6f5f
commit
3a26219cb8
@ -0,0 +1,73 @@
|
|||||||
|
#ifndef _INPUT_DISPATCHMAP_H
|
||||||
|
#define _INPUT_DISPATCHMAP_H
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
#include <map>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
namespace Input {
|
||||||
|
|
||||||
|
/**
|
||||||
|
DispatchMap is a simple connection system that connects incomming
|
||||||
|
signals with outgoing signals.
|
||||||
|
|
||||||
|
The signals can be connected one-to-one, many-to-one, one-to-many
|
||||||
|
or many-to-many.
|
||||||
|
|
||||||
|
The dispatch map is completely system agnostic. It is a pure data
|
||||||
|
structure and all signals are just integer indices. It does not
|
||||||
|
delegate any actions, but used together with Dispatcher it can be
|
||||||
|
used to build an event system.
|
||||||
|
*/
|
||||||
|
struct DispatchMap
|
||||||
|
{
|
||||||
|
typedef std::set<int> OutList;
|
||||||
|
typedef std::map<int, OutList> InMap;
|
||||||
|
|
||||||
|
typedef OutList::iterator Oit;
|
||||||
|
typedef InMap::iterator Iit;
|
||||||
|
|
||||||
|
InMap map;
|
||||||
|
|
||||||
|
void bind(int in, int out)
|
||||||
|
{
|
||||||
|
map[in].insert(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
void unbind(int in, int out)
|
||||||
|
{
|
||||||
|
Iit it = map.find(in);
|
||||||
|
if(it != map.end())
|
||||||
|
{
|
||||||
|
it->second.erase(out);
|
||||||
|
|
||||||
|
// If there are no more elements, then remove the entire list
|
||||||
|
if(it->second.empty())
|
||||||
|
map.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if a given input is bound to anything
|
||||||
|
bool isBound(int in)
|
||||||
|
{
|
||||||
|
return map.find(in) != map.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Get the list of outputs bound to the given input. Only call this
|
||||||
|
on inputs that you know are bound to something.
|
||||||
|
|
||||||
|
The returned set is only intended for immediate iteration. Do not
|
||||||
|
store references to it.
|
||||||
|
*/
|
||||||
|
const OutList &getList(int in)
|
||||||
|
{
|
||||||
|
assert(isBound(in));
|
||||||
|
const OutList &out = map[in];
|
||||||
|
assert(!out.empty());
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
@ -0,0 +1,78 @@
|
|||||||
|
#ifndef _INPUT_EVENT_DISPATCHER_H
|
||||||
|
#define _INPUT_EVENT_DISPATCHER_H
|
||||||
|
|
||||||
|
#include "dispatch_map.hpp"
|
||||||
|
#include <boost/function.hpp>
|
||||||
|
|
||||||
|
namespace Input {
|
||||||
|
|
||||||
|
/**
|
||||||
|
The EventDispatcher translates an input event (as given by an
|
||||||
|
identifying index and an optional void pointer) into an output
|
||||||
|
event function call.
|
||||||
|
*/
|
||||||
|
class EventDispatcher
|
||||||
|
{
|
||||||
|
DispatchMap map;
|
||||||
|
|
||||||
|
/*
|
||||||
|
The event callback function that is called for all events. The
|
||||||
|
first parameter is the input event. The second parameter is the
|
||||||
|
resolved output event. The third is an optional user-defined
|
||||||
|
parameter passed to call().
|
||||||
|
*/
|
||||||
|
typedef boost::function<void(int,int,void*)> EventCallback;
|
||||||
|
|
||||||
|
EventCallback callback;
|
||||||
|
|
||||||
|
// Carpal-tunnel prevention
|
||||||
|
typedef DispatchMap::OutList _OL;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// Create an event binding connection
|
||||||
|
void bind(int in, int out)
|
||||||
|
{ map.bind(in,out); }
|
||||||
|
|
||||||
|
/// Dissolve an event binding connection
|
||||||
|
void unbind(int in, int out)
|
||||||
|
{ map.unbind(in, out); }
|
||||||
|
|
||||||
|
/// Check if a given input is bound to anything
|
||||||
|
bool isBound(int in)
|
||||||
|
{ return map.isBound(in); }
|
||||||
|
|
||||||
|
/// Register the callback you want to use to handle events.
|
||||||
|
void setCallback(EventCallback cb)
|
||||||
|
{ callback = cb; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
Instigate an event.
|
||||||
|
|
||||||
|
This will look up the input event number (first parameter), and
|
||||||
|
call the event callback for each output number associated with
|
||||||
|
(bound to) that input.
|
||||||
|
|
||||||
|
The optional second paramter is also passed to the callback.
|
||||||
|
|
||||||
|
If no output is bound to the given event number, the callback
|
||||||
|
is never called.
|
||||||
|
*/
|
||||||
|
void call(int event, void *p = NULL)
|
||||||
|
{
|
||||||
|
// You have to set the callback before using call().
|
||||||
|
assert(!callback.empty());
|
||||||
|
|
||||||
|
// Not bound? Exit.
|
||||||
|
if(!isBound(event)) return;
|
||||||
|
|
||||||
|
// Dispatch to all events.
|
||||||
|
const _OL &list = map.getList(event);
|
||||||
|
for(_OL::const_iterator it = list.begin();
|
||||||
|
it != list.end(); it++)
|
||||||
|
callback(event, *it, p);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
@ -0,0 +1,99 @@
|
|||||||
|
#ifndef _INPUT_FUNCBINDER_H
|
||||||
|
#define _INPUT_FUNCBINDER_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <boost/function.hpp>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
namespace Input {
|
||||||
|
|
||||||
|
/**
|
||||||
|
An Action defines the user defined action corresponding to a
|
||||||
|
binding.
|
||||||
|
|
||||||
|
The first parameter is the action index that invoked this call. You
|
||||||
|
can assign the same function to multiple actions, and this can help
|
||||||
|
you keep track of which action was invoked.
|
||||||
|
|
||||||
|
The second parameter is an optional user-defined parameter,
|
||||||
|
represented by a void pointer. In many cases it is practical to
|
||||||
|
point this to temporaries (stack values), so make sure not to store
|
||||||
|
permanent references to it unless you've planning for this on the
|
||||||
|
calling side as well.
|
||||||
|
*/
|
||||||
|
typedef boost::function<void(int,void*)> Action;
|
||||||
|
|
||||||
|
/**
|
||||||
|
The FuncBinder is a simple struct that binds user-defined indices
|
||||||
|
to functions. It is useful for binding eg. keyboard events to
|
||||||
|
specific actions in your program, but can potentially have many
|
||||||
|
other uses as well.
|
||||||
|
*/
|
||||||
|
class FuncBinder
|
||||||
|
{
|
||||||
|
struct FuncBinding
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
Action action;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<FuncBinding> bindings;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
Constructor. Initialize the struct by telling it how many action
|
||||||
|
indices you intend to bind.
|
||||||
|
|
||||||
|
The indices you use should be 0 <= i < number.
|
||||||
|
*/
|
||||||
|
FuncBinder(int number) : bindings(number) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Bind an action to an index.
|
||||||
|
*/
|
||||||
|
void bind(int index, Action action, const std::string &name="")
|
||||||
|
{
|
||||||
|
assert(index >= 0 && index < bindings.size());
|
||||||
|
FuncBinding &fb = bindings[index];
|
||||||
|
fb.action = action;
|
||||||
|
fb.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Unbind an index, reverting a previous bind().
|
||||||
|
*/
|
||||||
|
void unbind(int index)
|
||||||
|
{
|
||||||
|
assert(index >= 0 && index < bindings.size());
|
||||||
|
bindings[index] = FuncBinding();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Call a specific action. Takes an optional parameter that is
|
||||||
|
passed to the action.
|
||||||
|
*/
|
||||||
|
void call(int index, void *p=NULL)
|
||||||
|
{
|
||||||
|
assert(index >= 0 && index < bindings.size());
|
||||||
|
FuncBinding &fb = bindings[index];
|
||||||
|
if(fb.action) fb.action(index, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if a given index is bound to anything
|
||||||
|
bool isBound(int index)
|
||||||
|
{
|
||||||
|
assert(index >= 0 && index < bindings.size());
|
||||||
|
return !bindings[index].action.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the name associated with an action (empty if not bound)
|
||||||
|
const std::string &getName(int index)
|
||||||
|
{
|
||||||
|
assert(index >= 0 && index < bindings.size());
|
||||||
|
return bindings[index].name;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
@ -1,9 +1,15 @@
|
|||||||
GCC=g++
|
GCC=g++
|
||||||
|
|
||||||
all: funcbind_test
|
all: funcbind_test dispatch_map_test event_dispatcher_test
|
||||||
|
|
||||||
funcbind_test: funcbind_test.cpp
|
funcbind_test: funcbind_test.cpp ../func_binder.hpp
|
||||||
$(GCC) $^ -o $@
|
$(GCC) $< -o $@
|
||||||
|
|
||||||
|
dispatch_map_test: dispatch_map_test.cpp ../dispatch_map.hpp
|
||||||
|
$(GCC) $< -o $@
|
||||||
|
|
||||||
|
event_dispatcher_test: event_dispatcher_test.cpp ../event_dispatcher.hpp
|
||||||
|
$(GCC) $< -o $@
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm *_test
|
rm *_test
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
#include <iostream>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#include "../dispatch_map.hpp"
|
||||||
|
|
||||||
|
using namespace Input;
|
||||||
|
|
||||||
|
typedef DispatchMap::OutList OutList;
|
||||||
|
typedef OutList::const_iterator Cit;
|
||||||
|
|
||||||
|
void showList(const DispatchMap::OutList &out)
|
||||||
|
{
|
||||||
|
for(Cit it = out.begin();
|
||||||
|
it != out.end(); it++)
|
||||||
|
{
|
||||||
|
cout << " " << *it << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void showAll(DispatchMap &map)
|
||||||
|
{
|
||||||
|
cout << "\nPrinting everything:\n";
|
||||||
|
for(DispatchMap::Iit it = map.map.begin();
|
||||||
|
it != map.map.end(); it++)
|
||||||
|
{
|
||||||
|
cout << it->first << ":\n";
|
||||||
|
showList(map.getList(it->first));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
cout << "Testing the dispatch map\n";
|
||||||
|
|
||||||
|
DispatchMap dsp;
|
||||||
|
|
||||||
|
dsp.bind(1,9);
|
||||||
|
dsp.bind(2,-5);
|
||||||
|
dsp.bind(2,9);
|
||||||
|
dsp.bind(3,10);
|
||||||
|
dsp.bind(3,12);
|
||||||
|
dsp.bind(3,10);
|
||||||
|
|
||||||
|
showAll(dsp);
|
||||||
|
|
||||||
|
dsp.unbind(1,9);
|
||||||
|
dsp.unbind(5,8);
|
||||||
|
dsp.unbind(3,11);
|
||||||
|
dsp.unbind(3,12);
|
||||||
|
dsp.unbind(3,12);
|
||||||
|
|
||||||
|
showAll(dsp);
|
||||||
|
return 0;
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
#include <iostream>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#include "../event_dispatcher.hpp"
|
||||||
|
|
||||||
|
using namespace Input;
|
||||||
|
|
||||||
|
void callback(int in, int out, void *p)
|
||||||
|
{
|
||||||
|
cout << " Got event: in=" << in << " out=" << out << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
EventDispatcher dsp;
|
||||||
|
|
||||||
|
void callAll()
|
||||||
|
{
|
||||||
|
cout << "\nDuty calls:\n";
|
||||||
|
for(int i=1; i<5; i++)
|
||||||
|
{
|
||||||
|
cout << " Calling event " << i << ":\n";
|
||||||
|
dsp.call(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
cout << "Testing the event dispatcher\n";
|
||||||
|
|
||||||
|
dsp.setCallback(&callback);
|
||||||
|
|
||||||
|
callAll();
|
||||||
|
|
||||||
|
dsp.bind(2,1);
|
||||||
|
dsp.bind(1,10);
|
||||||
|
dsp.bind(14,-12);
|
||||||
|
dsp.bind(2,-137);
|
||||||
|
|
||||||
|
callAll();
|
||||||
|
|
||||||
|
dsp.unbind(1,8);
|
||||||
|
dsp.unbind(1,10);
|
||||||
|
dsp.unbind(2,-137);
|
||||||
|
dsp.unbind(2,1);
|
||||||
|
|
||||||
|
callAll();
|
||||||
|
|
||||||
|
dsp.bind(3, 19);
|
||||||
|
dsp.bind(4, 18);
|
||||||
|
dsp.bind(4, 18);
|
||||||
|
|
||||||
|
callAll();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -1,84 +1,47 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
#include <string>
|
#include "../func_binder.hpp"
|
||||||
#include <vector>
|
|
||||||
#include <boost/function.hpp>
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
typedef boost::function<void()> Action;
|
void f1(int i, void *p)
|
||||||
|
|
||||||
struct FuncBind
|
|
||||||
{
|
|
||||||
std::string name;
|
|
||||||
Action action;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Binder
|
|
||||||
{
|
{
|
||||||
std::vector<FuncBind> bindings;
|
cout << " F1 i=" << i << endl;
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
Initialize the struct by telling it how many functions you have
|
|
||||||
to bind. The largest index you intend to use should be number-1.
|
|
||||||
*/
|
|
||||||
Binder(int number) : bindings(number) {}
|
|
||||||
|
|
||||||
void bind(int index, Action action, const std::string &name="")
|
|
||||||
{
|
|
||||||
assert(index >= 0 && index < bindings.size());
|
|
||||||
FuncBind &fb = bindings[index];
|
|
||||||
fb.action = action;
|
|
||||||
fb.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
void unbind(int index)
|
|
||||||
{
|
|
||||||
assert(index >= 0 && index < bindings.size());
|
|
||||||
FuncBind &fb = bindings[index];
|
|
||||||
fb = FuncBind();
|
|
||||||
}
|
|
||||||
|
|
||||||
void call(int index)
|
if(p)
|
||||||
{
|
cout << " Got a nice gift: "
|
||||||
assert(index >= 0 && index < bindings.size());
|
<< *((float*)p) << endl;
|
||||||
FuncBind &fb = bindings[index];
|
|
||||||
if(fb.action)
|
|
||||||
{
|
|
||||||
cout << "Calling '" << fb.name << "'\n";
|
|
||||||
fb.action();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
cout << "No function\n";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void f1()
|
|
||||||
{
|
|
||||||
cout << "In f1()\n";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void f2()
|
void f2(int i, void *p)
|
||||||
{
|
{
|
||||||
cout << "In f2()\n";
|
cout << " F2 i=" << i << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using namespace Input;
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
cout << "This will test the function binding system\n";
|
cout << "This will test the function binding system\n";
|
||||||
|
|
||||||
Binder bnd(5);
|
FuncBinder bnd(5);
|
||||||
|
|
||||||
bnd.bind(0, &f1, "This is action 1");
|
bnd.bind(0, &f1, "This is action 1");
|
||||||
bnd.bind(1, &f2);
|
bnd.bind(1, &f2);
|
||||||
bnd.bind(2, &f1, "This is action 3");
|
bnd.bind(2, &f1, "This is action 3");
|
||||||
|
bnd.bind(3, &f2, "This is action 4");
|
||||||
|
|
||||||
|
bnd.unbind(2);
|
||||||
|
|
||||||
for(int i=0; i<5; i++)
|
for(int i=0; i<5; i++)
|
||||||
{
|
{
|
||||||
cout << "\nCalling " << i << endl;
|
cout << "Calling " << i << ": '" << bnd.getName(i) << "'\n";
|
||||||
bnd.call(i);
|
bnd.call(i);
|
||||||
|
if(!bnd.isBound(i)) cout << " (not bound)\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cout << "\nCalling with parameter:\n";
|
||||||
|
float f = 3.1415;
|
||||||
|
bnd.call(0, &f);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
Testing the dispatch map
|
||||||
|
|
||||||
|
Printing everything:
|
||||||
|
1:
|
||||||
|
9
|
||||||
|
2:
|
||||||
|
-5
|
||||||
|
9
|
||||||
|
3:
|
||||||
|
10
|
||||||
|
12
|
||||||
|
|
||||||
|
Printing everything:
|
||||||
|
2:
|
||||||
|
-5
|
||||||
|
9
|
||||||
|
3:
|
||||||
|
10
|
@ -0,0 +1,30 @@
|
|||||||
|
Testing the event dispatcher
|
||||||
|
|
||||||
|
Duty calls:
|
||||||
|
Calling event 1:
|
||||||
|
Calling event 2:
|
||||||
|
Calling event 3:
|
||||||
|
Calling event 4:
|
||||||
|
|
||||||
|
Duty calls:
|
||||||
|
Calling event 1:
|
||||||
|
Got event: in=1 out=10
|
||||||
|
Calling event 2:
|
||||||
|
Got event: in=2 out=-137
|
||||||
|
Got event: in=2 out=1
|
||||||
|
Calling event 3:
|
||||||
|
Calling event 4:
|
||||||
|
|
||||||
|
Duty calls:
|
||||||
|
Calling event 1:
|
||||||
|
Calling event 2:
|
||||||
|
Calling event 3:
|
||||||
|
Calling event 4:
|
||||||
|
|
||||||
|
Duty calls:
|
||||||
|
Calling event 1:
|
||||||
|
Calling event 2:
|
||||||
|
Calling event 3:
|
||||||
|
Got event: in=3 out=19
|
||||||
|
Calling event 4:
|
||||||
|
Got event: in=4 out=18
|
@ -1,19 +1,15 @@
|
|||||||
This will test the function binding system
|
This will test the function binding system
|
||||||
|
Calling 0: 'This is action 1'
|
||||||
Calling 0
|
F1 i=0
|
||||||
Calling 'This is action 1'
|
Calling 1: ''
|
||||||
In f1()
|
F2 i=1
|
||||||
|
Calling 2: ''
|
||||||
Calling 1
|
(not bound)
|
||||||
Calling ''
|
Calling 3: 'This is action 4'
|
||||||
In f2()
|
F2 i=3
|
||||||
|
Calling 4: ''
|
||||||
Calling 2
|
(not bound)
|
||||||
Calling 'This is action 3'
|
|
||||||
In f1()
|
Calling with parameter:
|
||||||
|
F1 i=0
|
||||||
Calling 3
|
Got a nice gift: 3.1415
|
||||||
No function
|
|
||||||
|
|
||||||
Calling 4
|
|
||||||
No function
|
|
||||||
|
Loading…
Reference in New Issue