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++
|
||||
|
||||
all: funcbind_test
|
||||
all: funcbind_test dispatch_map_test event_dispatcher_test
|
||||
|
||||
funcbind_test: funcbind_test.cpp
|
||||
$(GCC) $^ -o $@
|
||||
funcbind_test: funcbind_test.cpp ../func_binder.hpp
|
||||
$(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:
|
||||
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>
|
||||
using namespace std;
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <boost/function.hpp>
|
||||
#include <assert.h>
|
||||
#include "../func_binder.hpp"
|
||||
|
||||
typedef boost::function<void()> Action;
|
||||
|
||||
struct FuncBind
|
||||
{
|
||||
std::string name;
|
||||
Action action;
|
||||
};
|
||||
|
||||
class Binder
|
||||
void f1(int i, void *p)
|
||||
{
|
||||
std::vector<FuncBind> bindings;
|
||||
|
||||
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();
|
||||
}
|
||||
cout << " F1 i=" << i << endl;
|
||||
|
||||
void call(int index)
|
||||
{
|
||||
assert(index >= 0 && index < bindings.size());
|
||||
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";
|
||||
if(p)
|
||||
cout << " Got a nice gift: "
|
||||
<< *((float*)p) << endl;
|
||||
}
|
||||
|
||||
void f2()
|
||||
void f2(int i, void *p)
|
||||
{
|
||||
cout << "In f2()\n";
|
||||
cout << " F2 i=" << i << endl;
|
||||
}
|
||||
|
||||
using namespace Input;
|
||||
|
||||
int main()
|
||||
{
|
||||
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(1, &f2);
|
||||
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++)
|
||||
{
|
||||
cout << "\nCalling " << i << endl;
|
||||
cout << "Calling " << i << ": '" << bnd.getName(i) << "'\n";
|
||||
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;
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
Calling 0
|
||||
Calling 'This is action 1'
|
||||
In f1()
|
||||
|
||||
Calling 1
|
||||
Calling ''
|
||||
In f2()
|
||||
|
||||
Calling 2
|
||||
Calling 'This is action 3'
|
||||
In f1()
|
||||
|
||||
Calling 3
|
||||
No function
|
||||
|
||||
Calling 4
|
||||
No function
|
||||
Calling 0: 'This is action 1'
|
||||
F1 i=0
|
||||
Calling 1: ''
|
||||
F2 i=1
|
||||
Calling 2: ''
|
||||
(not bound)
|
||||
Calling 3: 'This is action 4'
|
||||
F2 i=3
|
||||
Calling 4: ''
|
||||
(not bound)
|
||||
|
||||
Calling with parameter:
|
||||
F1 i=0
|
||||
Got a nice gift: 3.1415
|
||||
|
Loading…
Reference in New Issue