Added input component

actorid
Nicolay Korslund 15 years ago
parent 1af84ad852
commit 5d73b47cc0

1
.gitignore vendored

@ -1,2 +1,3 @@
*~
*.o
*_test

3
.gitmodules vendored

@ -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…
Cancel
Save