1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-01-16 18:19:55 +00:00

Added input component

This commit is contained in:
Nicolay Korslund 2010-07-04 14:28:51 +02:00
parent 1af84ad852
commit 5d73b47cc0
18 changed files with 570 additions and 0 deletions

1
.gitignore vendored
View file

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

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "mangle"]
path = mangle
url = git://github.com/korslund/mangle

75
input/dispatch_map.hpp Normal file
View file

@ -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

49
input/dispatcher.hpp Normal file
View file

@ -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

104
input/func_binder.hpp Normal file
View file

@ -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

46
input/poller.hpp Normal file
View file

@ -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

18
input/tests/Makefile Normal file
View file

@ -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

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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

View file

@ -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

View file

@ -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!

View file

@ -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!

View file

@ -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;
}

View file

@ -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;
}

18
input/tests/test.sh Executable file
View file

@ -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

1
mangle Submodule

@ -0,0 +1 @@
Subproject commit 7583fc3f1bfa6d0fde56c925da959a5e9e50031a

11
testall.sh Executable file
View file

@ -0,0 +1,11 @@
#!/bin/bash
function run()
{
echo "TESTING $1"
cd "$1/tests/"
./test.sh
cd ../../
}
run input