Added input component
parent
1af84ad852
commit
5d73b47cc0
@ -1,2 +1,3 @@
|
||||
*~
|
||||
*.o
|
||||
*_test
|
||||
|
@ -0,0 +1,3 @@
|
||||
[submodule "mangle"]
|
||||
path = mangle
|
||||
url = git://github.com/korslund/mangle
|
@ -0,0 +1,75 @@
|
||||
#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) const
|
||||
{
|
||||
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) const
|
||||
{
|
||||
assert(isBound(in));
|
||||
InMap::const_iterator it = map.find(in);
|
||||
assert(it != map.end());
|
||||
const OutList &out = it->second;
|
||||
assert(!out.empty());
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
@ -0,0 +1,49 @@
|
||||
#ifndef _INPUT_DISPATCHER_H
|
||||
#define _INPUT_DISPATCHER_H
|
||||
|
||||
#include "dispatch_map.hpp"
|
||||
#include "func_binder.hpp"
|
||||
#include <mangle/input/event.hpp>
|
||||
|
||||
namespace Input {
|
||||
|
||||
struct Dispatcher : Mangle::Input::Event
|
||||
{
|
||||
DispatchMap map;
|
||||
FuncBinder funcs;
|
||||
|
||||
/**
|
||||
Constructor. Takes the number of actions and passes it to
|
||||
FuncBinder.
|
||||
*/
|
||||
Dispatcher(int actions) : funcs(actions) {}
|
||||
|
||||
void bind(int action, int key) { map.bind(key, action); }
|
||||
void unbind(int action, int key) { map.unbind(key, action); }
|
||||
bool isBound(int key) const { return map.isBound(key); }
|
||||
|
||||
/**
|
||||
Instigate an event. It is translated through the dispatch map and
|
||||
sent to the function bindings.
|
||||
*/
|
||||
typedef DispatchMap::OutList _O;
|
||||
void event(Type type, int index, const void* p)
|
||||
{
|
||||
// No bindings, nothing happens
|
||||
if(!isBound(index))
|
||||
return;
|
||||
|
||||
// Only treat key-down events for now
|
||||
if(type != EV_KeyDown)
|
||||
return;
|
||||
|
||||
// Get the mapped actions and execute them
|
||||
const _O &list = map.getList(index);
|
||||
_O::const_iterator it;
|
||||
for(it = list.begin(); it != list.end(); it++)
|
||||
funcs.call(*it, p);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
@ -0,0 +1,104 @@
|
||||
#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,const 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 < (int)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 < (int)bindings.size());
|
||||
|
||||
bindings[index] = FuncBinding();
|
||||
}
|
||||
|
||||
/**
|
||||
Call a specific action. Takes an optional parameter that is
|
||||
passed to the action.
|
||||
*/
|
||||
void call(int index, const void *p=NULL) const
|
||||
{
|
||||
assert(index >= 0 && index < (int)bindings.size());
|
||||
|
||||
const FuncBinding &fb = bindings[index];
|
||||
if(fb.action) fb.action(index, p);
|
||||
}
|
||||
|
||||
/// Check if a given index is bound to anything
|
||||
bool isBound(int index) const
|
||||
{
|
||||
assert(index >= 0 && index < (int)bindings.size());
|
||||
|
||||
return !bindings[index].action.empty();
|
||||
}
|
||||
|
||||
/// Return the name associated with an action (empty if not bound)
|
||||
const std::string &getName(int index) const
|
||||
{
|
||||
assert(index >= 0 && index < (int)bindings.size());
|
||||
|
||||
return bindings[index].name;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
@ -0,0 +1,46 @@
|
||||
#ifndef _INPUT_POLLER_H
|
||||
#define _INPUT_POLLER_H
|
||||
|
||||
#include "dispatch_map.hpp"
|
||||
#include <mangle/input/driver.hpp>
|
||||
|
||||
namespace Input {
|
||||
|
||||
/** The poller is used to check (poll) for keys rather than waiting
|
||||
for events. */
|
||||
struct Poller
|
||||
{
|
||||
DispatchMap map;
|
||||
Mangle::Input::Driver &input;
|
||||
|
||||
Poller(Mangle::Input::Driver &drv)
|
||||
: input(drv) {}
|
||||
|
||||
/** Bind or unbind a given action with a key. The action is the first
|
||||
parameter, the key is the second.
|
||||
*/
|
||||
void bind(int in, int out) { map.bind(in, out); }
|
||||
void unbind(int in, int out) { map.unbind(in, out); }
|
||||
bool isBound(int in) const { return map.isBound(in); }
|
||||
|
||||
/// Check whether a given action button is currently pressed.
|
||||
typedef DispatchMap::OutList _O;
|
||||
bool isDown(int index) const
|
||||
{
|
||||
// No bindings, no action
|
||||
if(!isBound(index))
|
||||
return false;
|
||||
|
||||
// Get all the keys bound to this action, and check them.
|
||||
const _O &list = map.getList(index);
|
||||
_O::const_iterator it;
|
||||
for(it = list.begin(); it != list.end(); it++)
|
||||
// If there's any match, we're good to go.
|
||||
if(input.isDown(*it)) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
@ -0,0 +1,18 @@
|
||||
GCC=g++
|
||||
|
||||
all: funcbind_test dispatch_map_test sdl_driver_test sdl_binder_test
|
||||
|
||||
funcbind_test: funcbind_test.cpp ../func_binder.hpp
|
||||
$(GCC) $< -o $@
|
||||
|
||||
dispatch_map_test: dispatch_map_test.cpp ../dispatch_map.hpp
|
||||
$(GCC) $< -o $@
|
||||
|
||||
sdl_driver_test: sdl_driver_test.cpp
|
||||
$(GCC) $< ../../mangle/input/servers/sdl_driver.cpp -o $@ -I/usr/include/SDL/ -lSDL -I../../
|
||||
|
||||
sdl_binder_test: sdl_binder_test.cpp
|
||||
$(GCC) $< ../../mangle/input/servers/sdl_driver.cpp -o $@ -I/usr/include/SDL/ -lSDL -I../../
|
||||
|
||||
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,47 @@
|
||||
#include <iostream>
|
||||
using namespace std;
|
||||
|
||||
#include "../func_binder.hpp"
|
||||
|
||||
void f1(int i, const void *p)
|
||||
{
|
||||
cout << " F1 i=" << i << endl;
|
||||
|
||||
if(p)
|
||||
cout << " Got a nice gift: "
|
||||
<< *((const float*)p) << endl;
|
||||
}
|
||||
|
||||
void f2(int i, const void *p)
|
||||
{
|
||||
cout << " F2 i=" << i << endl;
|
||||
}
|
||||
|
||||
using namespace Input;
|
||||
|
||||
int main()
|
||||
{
|
||||
cout << "This will test the function binding system\n";
|
||||
|
||||
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 << "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,15 @@
|
||||
This will test the function binding system
|
||||
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
|
@ -0,0 +1,4 @@
|
||||
Hold the Q key to quit:
|
||||
You are running in script mode, aborting. Run this test with a parameter (any at all) to test the input loop properly
|
||||
|
||||
Bye bye!
|
@ -0,0 +1,4 @@
|
||||
Hold the Q key to quit:
|
||||
You are running in script mode, aborting. Run this test with a parameter (any at all) to test the input loop properly
|
||||
|
||||
Bye bye!
|
@ -0,0 +1,71 @@
|
||||
#include <iostream>
|
||||
#include <mangle/input/servers/sdl_driver.hpp>
|
||||
#include <SDL.h>
|
||||
#include "../dispatcher.hpp"
|
||||
#include "../poller.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace Mangle::Input;
|
||||
using namespace Input;
|
||||
|
||||
enum Actions
|
||||
{
|
||||
A_Quit,
|
||||
A_Left,
|
||||
A_Right,
|
||||
|
||||
A_LAST
|
||||
};
|
||||
|
||||
bool quit=false;
|
||||
|
||||
void doExit(int,const void*)
|
||||
{
|
||||
quit = true;
|
||||
}
|
||||
|
||||
void goLeft(int,const void*)
|
||||
{
|
||||
cout << "Going left\n";
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
SDL_Init(SDL_INIT_VIDEO);
|
||||
SDL_SetVideoMode(640, 480, 0, SDL_SWSURFACE);
|
||||
SDLDriver input;
|
||||
Dispatcher disp(A_LAST);
|
||||
Poller poll(input);
|
||||
|
||||
input.setEvent(&disp);
|
||||
|
||||
disp.funcs.bind(A_Quit, &doExit);
|
||||
disp.funcs.bind(A_Left, &goLeft);
|
||||
|
||||
disp.bind(A_Quit, SDLK_q);
|
||||
disp.bind(A_Left, SDLK_a);
|
||||
disp.bind(A_Left, SDLK_LEFT);
|
||||
|
||||
poll.bind(A_Right, SDLK_d);
|
||||
poll.bind(A_Right, SDLK_RIGHT);
|
||||
|
||||
cout << "Hold the Q key to quit:\n";
|
||||
//input->setEvent(&mycb);
|
||||
while(!quit)
|
||||
{
|
||||
input.capture();
|
||||
if(poll.isDown(A_Right))
|
||||
cout << "We're going right!\n";
|
||||
SDL_Delay(20);
|
||||
|
||||
if(argc == 1)
|
||||
{
|
||||
cout << "You are running in script mode, aborting. Run this test with a parameter (any at all) to test the input loop properly\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
cout << "\nBye bye!\n";
|
||||
|
||||
SDL_Quit();
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
#include <iostream>
|
||||
#include <mangle/input/servers/sdl_driver.hpp>
|
||||
#include <SDL.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace Mangle::Input;
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
SDL_Init(SDL_INIT_VIDEO);
|
||||
SDL_SetVideoMode(640, 480, 0, SDL_SWSURFACE);
|
||||
SDLDriver input;
|
||||
|
||||
cout << "Hold the Q key to quit:\n";
|
||||
//input->setEvent(&mycb);
|
||||
while(!input.isDown(SDLK_q))
|
||||
{
|
||||
input.capture();
|
||||
SDL_Delay(20);
|
||||
|
||||
if(argc == 1)
|
||||
{
|
||||
cout << "You are running in script mode, aborting. Run this test with a parameter (any at all) to test the input loop properly\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
cout << "\nBye bye!\n";
|
||||
|
||||
SDL_Quit();
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
|
||||
make || exit
|
||||
|
||||
mkdir -p output
|
||||
|
||||
PROGS=*_test
|
||||
|
||||
for a in $PROGS; do
|
||||
if [ -f "output/$a.out" ]; then
|
||||
echo "Running $a:"
|
||||
./$a | diff output/$a.out -
|
||||
else
|
||||
echo "Creating $a.out"
|
||||
./$a > "output/$a.out"
|
||||
git add "output/$a.out"
|
||||
fi
|
||||
done
|
@ -0,0 +1 @@
|
||||
Subproject commit 7583fc3f1bfa6d0fde56c925da959a5e9e50031a
|
@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
function run()
|
||||
{
|
||||
echo "TESTING $1"
|
||||
cd "$1/tests/"
|
||||
./test.sh
|
||||
cd ../../
|
||||
}
|
||||
|
||||
run input
|
Loading…
Reference in New Issue