Make joysticks actually work
This commit is contained in:
parent
6bb1685a36
commit
faafcd2c7d
6 changed files with 1166 additions and 998 deletions
14
command.cpp
14
command.cpp
|
@ -82,11 +82,6 @@ command::~command() throw()
|
|||
void command::invokeC(const std::string& cmd) throw()
|
||||
{
|
||||
try {
|
||||
if(command_stack.count(cmd)) {
|
||||
messages << "Can not invoke recursively: " << cmd << std::endl;
|
||||
return;
|
||||
}
|
||||
command_stack.insert(cmd);
|
||||
std::string cmd2 = cmd;
|
||||
if(cmd2 == "?") {
|
||||
//The special ? command.
|
||||
|
@ -94,7 +89,6 @@ void command::invokeC(const std::string& cmd) throw()
|
|||
for(auto i = commands->begin(); i != commands->end(); ++i)
|
||||
messages << i->first << ": " << i->second->get_short_help() << std::endl;
|
||||
}
|
||||
command_stack.erase(cmd);
|
||||
return;
|
||||
}
|
||||
if(cmd2.length() > 1 && cmd2[0] == '?') {
|
||||
|
@ -114,7 +108,6 @@ void command::invokeC(const std::string& cmd) throw()
|
|||
size_t j = 0;
|
||||
for(auto i = aliases[aname].begin(); i != aliases[aname].end(); ++i, ++j)
|
||||
messages << "#" + (j + 1) << ": " << *i << std::endl;
|
||||
command_stack.erase(cmd);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -123,11 +116,9 @@ void command::invokeC(const std::string& cmd) throw()
|
|||
if(!commands || !commands->count(rcmd)) {
|
||||
if(rcmd != "")
|
||||
messages << "Unknown command '" << rcmd << "'" << std::endl;
|
||||
command_stack.erase(cmd);
|
||||
return;
|
||||
}
|
||||
messages << (*commands)[rcmd]->get_long_help() << std::endl;
|
||||
command_stack.erase(cmd);
|
||||
return;
|
||||
}
|
||||
bool may_be_alias_expanded = true;
|
||||
|
@ -138,7 +129,6 @@ void command::invokeC(const std::string& cmd) throw()
|
|||
if(may_be_alias_expanded && aliases.count(cmd2)) {
|
||||
for(auto i = aliases[cmd2].begin(); i != aliases[cmd2].end(); ++i)
|
||||
invokeC(*i);
|
||||
command_stack.erase(cmd);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
|
@ -157,9 +147,11 @@ void command::invokeC(const std::string& cmd) throw()
|
|||
cmdh = (*commands)[rcmd];
|
||||
if(!cmdh) {
|
||||
messages << "Unknown command '" << rcmd << "'" << std::endl;
|
||||
command_stack.erase(cmd);
|
||||
return;
|
||||
}
|
||||
if(command_stack.count(cmd))
|
||||
throw std::runtime_error("Recursive command invocation");
|
||||
command_stack.insert(cmd);
|
||||
cmdh->invoke(args);
|
||||
command_stack.erase(cmd);
|
||||
return;
|
||||
|
|
437
keymapper.cpp
437
keymapper.cpp
|
@ -1,9 +1,10 @@
|
|||
#include "keymapper.hpp"
|
||||
#include <stdexcept>
|
||||
#include "lua.hpp"
|
||||
#include "window.hpp"
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <set>
|
||||
#include "misc.hpp"
|
||||
#include "memorymanip.hpp"
|
||||
|
@ -30,7 +31,11 @@ namespace
|
|||
command = t.tail();
|
||||
if(command == "")
|
||||
throw std::runtime_error("Expected command");
|
||||
window::bind(mod, modmask, keyname, command);
|
||||
keymapper::bind(mod, modmask, keyname, command);
|
||||
if(mod != "" || modmask != "")
|
||||
messages << mod << "/" << modmask << " ";
|
||||
messages << keyname << " bound to '" << command << "'" << std::endl;
|
||||
|
||||
});
|
||||
|
||||
function_ptr_command<tokensplitter&> unbind_key("unbind-key", "Unbind a (pseudo-)key",
|
||||
|
@ -51,13 +56,16 @@ namespace
|
|||
command = t.tail();
|
||||
if(command != "")
|
||||
throw std::runtime_error("Unexpected argument");
|
||||
window::unbind(mod, modmask, keyname);
|
||||
keymapper::unbind(mod, modmask, keyname);
|
||||
if(mod != "" || modmask != "")
|
||||
messages << mod << "/" << modmask << " ";
|
||||
messages << keyname << " unbound" << std::endl;
|
||||
});
|
||||
|
||||
function_ptr_command<> show_bindings("show-bindings", "Show active bindings",
|
||||
"Syntax: show-bindings\nShow bindings that are currently active.\n",
|
||||
[]() throw(std::bad_alloc, std::runtime_error) {
|
||||
window::dumpbindings();
|
||||
keymapper::dumpbindings();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -71,3 +79,424 @@ std::string fixup_command_polarity(std::string cmd, bool polarity) throw(std::ba
|
|||
cmd[0] = '-';
|
||||
return cmd;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
std::map<std::string, modifier*>* known_modifiers;
|
||||
std::map<std::string, std::string>* modifier_linkages;
|
||||
std::map<std::string, keygroup*>* keygroups;
|
||||
|
||||
//Returns orig if not linked.
|
||||
const modifier* get_linked_modifier(const modifier* orig)
|
||||
{
|
||||
if(!modifier_linkages || !modifier_linkages->count(orig->name()))
|
||||
return orig;
|
||||
std::string l = (*modifier_linkages)[orig->name()];
|
||||
return (*known_modifiers)[l];
|
||||
}
|
||||
}
|
||||
|
||||
modifier::modifier(const std::string& name) throw(std::bad_alloc)
|
||||
{
|
||||
if(!known_modifiers)
|
||||
known_modifiers = new std::map<std::string, modifier*>();
|
||||
(*known_modifiers)[modname = name] = this;
|
||||
}
|
||||
|
||||
modifier::modifier(const std::string& name, const std::string& linkgroup) throw(std::bad_alloc)
|
||||
{
|
||||
if(!known_modifiers)
|
||||
known_modifiers = new std::map<std::string, modifier*>();
|
||||
if(!modifier_linkages)
|
||||
modifier_linkages = new std::map<std::string, std::string>();
|
||||
(*known_modifiers)[modname = name] = this;
|
||||
(*modifier_linkages)[name] = linkgroup;
|
||||
}
|
||||
|
||||
modifier& modifier::lookup(const std::string& name) throw(std::bad_alloc, std::runtime_error)
|
||||
{
|
||||
if(!known_modifiers || !known_modifiers->count(name)) {
|
||||
std::ostringstream x;
|
||||
x << "Invalid modifier '" << name << "'";
|
||||
throw std::runtime_error(x.str());
|
||||
}
|
||||
return *(*known_modifiers)[name];
|
||||
}
|
||||
|
||||
std::string modifier::name() const throw(std::bad_alloc)
|
||||
{
|
||||
return modname;
|
||||
}
|
||||
|
||||
|
||||
void modifier_set::add(const modifier& mod, bool really) throw(std::bad_alloc)
|
||||
{
|
||||
if(really)
|
||||
set.insert(&mod);
|
||||
}
|
||||
|
||||
void modifier_set::remove(const modifier& mod, bool really) throw(std::bad_alloc)
|
||||
{
|
||||
if(really)
|
||||
set.erase(&mod);
|
||||
}
|
||||
|
||||
modifier_set modifier_set::construct(const std::string& _modifiers) throw(std::bad_alloc, std::runtime_error)
|
||||
{
|
||||
modifier_set set;
|
||||
std::string modifiers = _modifiers;
|
||||
while(modifiers != "") {
|
||||
std::string mod = modifiers;
|
||||
std::string rest;
|
||||
size_t split = modifiers.find_first_of(",");
|
||||
if(split < modifiers.length()) {
|
||||
mod = modifiers.substr(0, split);
|
||||
rest = modifiers.substr(split + 1);
|
||||
}
|
||||
set.add(modifier::lookup(mod));
|
||||
modifiers = rest;
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
bool modifier_set::valid(const modifier_set& set, const modifier_set& mask) throw(std::bad_alloc)
|
||||
{
|
||||
//No element can be together with its linkage group.
|
||||
for(auto i = set.set.begin(); i != set.set.end(); ++i) {
|
||||
const modifier* j = get_linked_modifier(*i);
|
||||
if(*i != j && set.set.count(j))
|
||||
return false;
|
||||
}
|
||||
for(auto i = mask.set.begin(); i != mask.set.end(); ++i) {
|
||||
const modifier* j = get_linked_modifier(*i);
|
||||
if(*i != j && mask.set.count(j))
|
||||
return false;
|
||||
}
|
||||
//For every element of set, it or its linkage group must be in mask.
|
||||
for(auto i = set.set.begin(); i != set.set.end(); ++i) {
|
||||
const modifier* j = get_linked_modifier(*i);
|
||||
if(!mask.set.count(*i) && !mask.set.count(j))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool modifier_set::operator==(const modifier_set& m) const throw()
|
||||
{
|
||||
for(auto i = set.begin(); i != set.end(); ++i)
|
||||
if(!m.set.count(*i))
|
||||
return false;
|
||||
for(auto i = m.set.begin(); i != m.set.end(); ++i)
|
||||
if(!set.count(*i))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool modifier_set::triggers(const modifier_set& set, const modifier_set& trigger, const modifier_set& mask)
|
||||
throw(std::bad_alloc)
|
||||
{
|
||||
modifier_set unmet = trigger;
|
||||
modifier_set blank;
|
||||
for(auto i = set.set.begin(); i != set.set.end(); i++) {
|
||||
auto linked = get_linked_modifier(*i);
|
||||
if(mask.set.count(linked) && trigger.set.count(linked))
|
||||
unmet.remove(*linked);
|
||||
if(mask.set.count(linked) && trigger.set.count(*i))
|
||||
unmet.remove(**i);
|
||||
if(mask.set.count(*i) && trigger.set.count(*i))
|
||||
unmet.remove(**i);
|
||||
}
|
||||
return (unmet == blank);
|
||||
}
|
||||
|
||||
std::string keygroup::name() throw(std::bad_alloc)
|
||||
{
|
||||
return keyname;
|
||||
}
|
||||
|
||||
|
||||
keygroup::keygroup(const std::string& name, enum type t) throw(std::bad_alloc)
|
||||
{
|
||||
if(!keygroups)
|
||||
keygroups = new std::map<std::string, keygroup*>();
|
||||
(*keygroups)[keyname = name] = this;
|
||||
ktype = t;
|
||||
state = 0;
|
||||
cal_left = -32768;
|
||||
cal_center = 0;
|
||||
cal_right = 32767;
|
||||
cal_tolerance = 0.5;
|
||||
}
|
||||
|
||||
void keygroup::change_type(enum type t)
|
||||
{
|
||||
ktype = t;
|
||||
state = 0;
|
||||
}
|
||||
|
||||
std::pair<keygroup*, unsigned> keygroup::lookup(const std::string& name) throw(std::bad_alloc,
|
||||
std::runtime_error)
|
||||
{
|
||||
if(!keygroups)
|
||||
throw std::runtime_error("Invalid key");
|
||||
if(keygroups->count(name))
|
||||
return std::make_pair((*keygroups)[name], 0);
|
||||
std::string prefix = name;
|
||||
char letter = prefix[prefix.length() - 1];
|
||||
prefix = prefix.substr(0, prefix.length() - 1);
|
||||
if(!keygroups->count(prefix))
|
||||
throw std::runtime_error("Invalid key");
|
||||
keygroup* g = (*keygroups)[prefix];
|
||||
switch(letter) {
|
||||
case '+':
|
||||
case 'n':
|
||||
return std::make_pair(g, 0);
|
||||
case '-':
|
||||
case 'e':
|
||||
return std::make_pair(g, 1);
|
||||
case 's':
|
||||
return std::make_pair(g, 2);
|
||||
case 'w':
|
||||
return std::make_pair(g, 3);
|
||||
default:
|
||||
throw std::runtime_error("Invalid key");
|
||||
}
|
||||
}
|
||||
|
||||
void keygroup::change_calibration(short left, short center, short right, double tolerance)
|
||||
{
|
||||
cal_left = left;
|
||||
cal_center = center;
|
||||
cal_right = right;
|
||||
cal_tolerance = tolerance;
|
||||
}
|
||||
|
||||
double keygroup::compensate(short value)
|
||||
{
|
||||
if(ktype == KT_HAT || ktype == KT_KEY || ktype == KT_DISABLED)
|
||||
return value; //These can't be calibrated.
|
||||
if(value <= cal_left)
|
||||
return -1.0;
|
||||
else if(value >= cal_right)
|
||||
return 1.0;
|
||||
else if(value == cal_center)
|
||||
return 0.0;
|
||||
else if(value < cal_center)
|
||||
return (static_cast<double>(value) - cal_center) / (static_cast<double>(cal_center) - cal_left);
|
||||
else
|
||||
return (static_cast<double>(value) - cal_center) / (static_cast<double>(cal_right) - cal_center);
|
||||
}
|
||||
|
||||
double keygroup::compensate2(double value)
|
||||
{
|
||||
switch(ktype) {
|
||||
case KT_DISABLED:
|
||||
return 0; //Always neutral.
|
||||
case KT_KEY:
|
||||
case KT_HAT:
|
||||
return value; //No mapping.
|
||||
case KT_PRESSURE_0M:
|
||||
return -value;
|
||||
case KT_PRESSURE_0P:
|
||||
return value;
|
||||
case KT_PRESSURE_M0:
|
||||
return 1 + value;
|
||||
case KT_PRESSURE_MP:
|
||||
return (1 + value) / 2;
|
||||
case KT_PRESSURE_P0:
|
||||
return 1 - value;
|
||||
case KT_PRESSURE_PM:
|
||||
return (1 - value) / 2;
|
||||
case KT_AXIS_PAIR:
|
||||
return value;
|
||||
case KT_AXIS_PAIR_INVERSE:
|
||||
return -value;
|
||||
};
|
||||
}
|
||||
|
||||
void keygroup::set_position(short pos, const modifier_set& modifiers) throw()
|
||||
{
|
||||
double x = compensate2(compensate(pos));
|
||||
unsigned tmp;
|
||||
bool left, right, up, down;
|
||||
bool oleft, oright, oup, odown;
|
||||
switch(ktype) {
|
||||
case KT_DISABLED:
|
||||
return;
|
||||
case KT_KEY:
|
||||
case KT_PRESSURE_0M:
|
||||
case KT_PRESSURE_0P:
|
||||
case KT_PRESSURE_M0:
|
||||
case KT_PRESSURE_MP:
|
||||
case KT_PRESSURE_P0:
|
||||
case KT_PRESSURE_PM:
|
||||
tmp = (x >= cal_tolerance);
|
||||
run_listeners(modifiers, 0, true, (!state && tmp), x);
|
||||
run_listeners(modifiers, 0, false, (state && !tmp), x);
|
||||
state = tmp;
|
||||
break;
|
||||
case KT_AXIS_PAIR:
|
||||
case KT_AXIS_PAIR_INVERSE:
|
||||
if(x <= -cal_tolerance)
|
||||
tmp = 2;
|
||||
else if(x >= cal_tolerance)
|
||||
tmp = 1;
|
||||
else
|
||||
tmp = 0;
|
||||
run_listeners(modifiers, 0, false, state == 1 && tmp != 1, x);
|
||||
run_listeners(modifiers, 1, false, state == 2 && tmp != 2, x);
|
||||
run_listeners(modifiers, 0, true, tmp == 1 && state != 1, x);
|
||||
run_listeners(modifiers, 1, true, tmp == 2 && state != 2, x);
|
||||
state = tmp;
|
||||
break;
|
||||
case KT_HAT:
|
||||
left = ((pos & 8) != 0);
|
||||
right = ((pos & 2) != 0);
|
||||
up = ((pos & 1) != 0);
|
||||
down = ((pos & 4) != 0);
|
||||
oleft = ((state & 8) != 0);
|
||||
oright = ((state & 2) != 0);
|
||||
oup = ((state & 1) != 0);
|
||||
odown = ((state & 4) != 0);
|
||||
run_listeners(modifiers, 3, false, oleft && !left, x);
|
||||
run_listeners(modifiers, 1, false, oright && !right, x);
|
||||
run_listeners(modifiers, 0, false, oup && !up, x);
|
||||
run_listeners(modifiers, 2, false, odown && !down, x);
|
||||
run_listeners(modifiers, 2, true, !odown && down, x);
|
||||
run_listeners(modifiers, 0, true, !oup && up, x);
|
||||
run_listeners(modifiers, 1, true, !oright && right, x);
|
||||
run_listeners(modifiers, 3, true, !oleft && left, x);
|
||||
state = pos;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void keygroup::add_key_listener(key_listener& l) throw(std::bad_alloc)
|
||||
{
|
||||
listeners.push_back(&l);
|
||||
}
|
||||
|
||||
void keygroup::remove_key_listener(key_listener& l) throw(std::bad_alloc)
|
||||
{
|
||||
for(auto i = listeners.begin(); i != listeners.end(); ++i)
|
||||
if(*i == &l) {
|
||||
listeners.erase(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void keygroup::run_listeners(const modifier_set& modifiers, unsigned subkey, bool polarity, bool really, double x)
|
||||
{
|
||||
if(!really)
|
||||
return;
|
||||
std::string name = keyname;
|
||||
if(ktype == KT_AXIS_PAIR && subkey == 0)
|
||||
name = name + "+";
|
||||
if(ktype == KT_AXIS_PAIR && subkey == 1)
|
||||
name = name + "-";
|
||||
if(ktype == KT_HAT && subkey == 0)
|
||||
name = name + "n";
|
||||
if(ktype == KT_HAT && subkey == 1)
|
||||
name = name + "e";
|
||||
if(ktype == KT_HAT && subkey == 2)
|
||||
name = name + "s";
|
||||
if(ktype == KT_HAT && subkey == 3)
|
||||
name = name + "w";
|
||||
if(exclusive) {
|
||||
exclusive->key_event(modifiers, *this, subkey, polarity, name);
|
||||
return;
|
||||
}
|
||||
for(auto i = listeners.begin(); i != listeners.end(); ++i)
|
||||
(*i)->key_event(modifiers, *this, subkey, polarity, name);
|
||||
}
|
||||
|
||||
keygroup::key_listener* keygroup::exclusive;
|
||||
|
||||
void keygroup::set_exclusive_key_listener(key_listener* l) throw()
|
||||
{
|
||||
exclusive = l;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
struct triple
|
||||
{
|
||||
triple(const std::string& _a, const std::string& _b, const std::string& _c)
|
||||
{
|
||||
a = _a;
|
||||
b = _b;
|
||||
c = _c;
|
||||
}
|
||||
std::string a;
|
||||
std::string b;
|
||||
std::string c;
|
||||
bool operator==(const triple& t) const
|
||||
{
|
||||
bool x = (a == t.a && b == t.b && c == t.c);
|
||||
return x;
|
||||
}
|
||||
bool operator<(const triple& t) const
|
||||
{
|
||||
bool x = (a < t.a || (a == t.a && b < t.b) || (a == t.a && b == t.b && c < t.c));
|
||||
return x;
|
||||
}
|
||||
};
|
||||
struct keybind_data : public keygroup::key_listener
|
||||
{
|
||||
modifier_set mod;
|
||||
modifier_set modmask;
|
||||
keygroup* group;
|
||||
unsigned subkey;
|
||||
std::string command;
|
||||
void key_event(const modifier_set& modifiers, keygroup& keygroup, unsigned subkey, bool polarity,
|
||||
const std::string& name)
|
||||
{
|
||||
if(!modifier_set::triggers(modifiers, mod, modmask))
|
||||
return;
|
||||
std::string cmd = fixup_command_polarity(command, polarity);
|
||||
if(cmd == "")
|
||||
return;
|
||||
command::invokeC(cmd);
|
||||
}
|
||||
};
|
||||
|
||||
std::map<triple, keybind_data> keybindings;
|
||||
}
|
||||
|
||||
void keymapper::bind(std::string mod, std::string modmask, std::string keyname, std::string command)
|
||||
throw(std::bad_alloc, std::runtime_error)
|
||||
{
|
||||
triple k(mod, modmask, keyname);
|
||||
modifier_set _mod = modifier_set::construct(mod);
|
||||
modifier_set _modmask = modifier_set::construct(modmask);
|
||||
if(!modifier_set::valid(_mod, _modmask))
|
||||
throw std::runtime_error("Invalid modifiers");
|
||||
auto g = keygroup::lookup(keyname);
|
||||
if(!keybindings.count(k)) {
|
||||
keybindings[k].mod = _mod;
|
||||
keybindings[k].modmask = _modmask;
|
||||
keybindings[k].group = g.first;
|
||||
keybindings[k].subkey = g.second;
|
||||
g.first->add_key_listener(keybindings[k]);
|
||||
}
|
||||
keybindings[k].command = command;
|
||||
}
|
||||
void keymapper::unbind(std::string mod, std::string modmask, std::string keyname) throw(std::bad_alloc,
|
||||
std::runtime_error)
|
||||
{
|
||||
triple k(mod, modmask, keyname);
|
||||
if(!keybindings.count(k))
|
||||
throw std::runtime_error("Key is not bound");
|
||||
keybindings[k].group->remove_key_listener(keybindings[k]);
|
||||
keybindings.erase(k);
|
||||
}
|
||||
|
||||
void keymapper::dumpbindings() throw(std::bad_alloc)
|
||||
{
|
||||
for(auto i = keybindings.begin(); i != keybindings.end(); ++i) {
|
||||
messages << "bind-key ";
|
||||
if(i->first.a != "" || i->first.b != "")
|
||||
messages << i->first.a << "/" << i->first.b << " ";
|
||||
messages << i->first.c << std::endl;
|
||||
}
|
||||
}
|
||||
|
|
310
keymapper.hpp
310
keymapper.hpp
|
@ -10,143 +10,225 @@
|
|||
#include "misc.hpp"
|
||||
|
||||
/**
|
||||
* \brief Fixup command according to key polarity.
|
||||
*
|
||||
* Takes in a raw command and returns the command that should be actually executed given the key polarity.
|
||||
*
|
||||
* \param cmd Raw command.
|
||||
* \param polarity Polarity (True => Being pressed, False => Being released).
|
||||
* \return The fixed command, "" if no command should be executed.
|
||||
* \throws std::bad_alloc Not enough memory.
|
||||
* parameter cmd: Raw command.
|
||||
* parameter polarity: Polarity (True => Being pressed, False => Being released).
|
||||
* returns: The fixed command, "" if no command should be executed.
|
||||
* throws std::bad_alloc: Not enough memory.
|
||||
*/
|
||||
std::string fixup_command_polarity(std::string cmd, bool polarity) throw(std::bad_alloc);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* \brief Keyboard mapper.
|
||||
*
|
||||
* This class handles internals of mapping events from keyboard buttons and pseudo-buttons. The helper class T has
|
||||
* to have the following:
|
||||
*
|
||||
* unsigned T::mod_str(const std::string& mod): Translate modifiers set mod into modifier mask.
|
||||
* typedef T::internal_keysymbol: Key symbol to match against. Needs to have == operator available.
|
||||
* T::internal_keysymbol key_str(const std::string& keyname): Translate key name to key to match against.
|
||||
* typedef T::keysymbol: Key symbol from keyboard (or pseudo-button). Carries modifiers too.
|
||||
* unsigned mod_key(T::keysymbol key): Get modifier mask for keyboard key.
|
||||
* T::internal_keysymbol key_key(T::keysymbol key): Get key symbol to match against for given keyboard key.
|
||||
* std::string T::name_key(unsigned mod, unsigned modmask, T::internal_keysymbol key): Print name of key with mods.
|
||||
* Modifier.
|
||||
*/
|
||||
class modifier
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Create modifier.
|
||||
*/
|
||||
modifier(const std::string& name) throw(std::bad_alloc);
|
||||
/**
|
||||
* Create linked modifier.
|
||||
*/
|
||||
modifier(const std::string& name, const std::string& linkgroup) throw(std::bad_alloc);
|
||||
/**
|
||||
* Look up a modifier.
|
||||
*/
|
||||
static modifier& lookup(const std::string& name) throw(std::bad_alloc, std::runtime_error);
|
||||
/**
|
||||
* Get name of modifier.
|
||||
*/
|
||||
std::string name() const throw(std::bad_alloc);
|
||||
private:
|
||||
|
||||
modifier(const modifier&);
|
||||
modifier& operator=(const modifier&);
|
||||
std::string modname;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set of modifiers.
|
||||
*/
|
||||
class modifier_set
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Add a modifier.
|
||||
*/
|
||||
void add(const modifier& mod, bool really = true) throw(std::bad_alloc);
|
||||
/**
|
||||
* Remove a modifier.
|
||||
*/
|
||||
void remove(const modifier& mod, bool really = true) throw(std::bad_alloc);
|
||||
/**
|
||||
* Construct set from string.
|
||||
*/
|
||||
static modifier_set construct(const std::string& modifiers) throw(std::bad_alloc, std::runtime_error);
|
||||
/**
|
||||
* Check modifier against its mask for validity.
|
||||
*/
|
||||
static bool valid(const modifier_set& set, const modifier_set& mask) throw(std::bad_alloc);
|
||||
/**
|
||||
* Check if this modifier set triggers the action.
|
||||
*/
|
||||
static bool triggers(const modifier_set& set, const modifier_set& trigger, const modifier_set& mask)
|
||||
throw(std::bad_alloc);
|
||||
/**
|
||||
* Equality.
|
||||
*/
|
||||
bool operator==(const modifier_set& m) const throw();
|
||||
|
||||
private:
|
||||
std::set<const modifier*> set;
|
||||
};
|
||||
|
||||
/**
|
||||
* Key or key group.
|
||||
*/
|
||||
class keygroup
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Key group type.
|
||||
*/
|
||||
enum type
|
||||
{
|
||||
/**
|
||||
* Disabled.
|
||||
*/
|
||||
KT_DISABLED,
|
||||
/**
|
||||
* Singular button.
|
||||
*/
|
||||
KT_KEY,
|
||||
/**
|
||||
* Pressure-sensitive button
|
||||
*/
|
||||
KT_PRESSURE_PM,
|
||||
KT_PRESSURE_MP,
|
||||
KT_PRESSURE_0P,
|
||||
KT_PRESSURE_0M,
|
||||
KT_PRESSURE_P0,
|
||||
KT_PRESSURE_M0,
|
||||
/**
|
||||
* Axis key pair.
|
||||
*/
|
||||
KT_AXIS_PAIR,
|
||||
KT_AXIS_PAIR_INVERSE,
|
||||
/**
|
||||
* Hat.
|
||||
*/
|
||||
KT_HAT
|
||||
};
|
||||
/**
|
||||
* Create new key group.
|
||||
*/
|
||||
keygroup(const std::string& name, enum type t) throw(std::bad_alloc);
|
||||
/**
|
||||
* Change type of key group.
|
||||
*/
|
||||
void change_type(enum type t);
|
||||
/**
|
||||
* Change calibration (Axis pairs and pressure buttons only).
|
||||
*/
|
||||
void change_calibration(short left, short center, short right, double tolerance);
|
||||
/**
|
||||
* Change state of this key group.
|
||||
*
|
||||
* For KT_KEY, value is zero/nonzero.
|
||||
* For KT_PRESSURE_* and KT_AXIS_PAIR*, value is -32768...32767.
|
||||
* For KT_HAT, 1 is up, 2 is right, 4 is down, 8 is left (may be ORed).
|
||||
*/
|
||||
void set_position(short pos, const modifier_set& modifiers) throw();
|
||||
/**
|
||||
* Look up key by name.
|
||||
*/
|
||||
static std::pair<keygroup*, unsigned> lookup(const std::string& name) throw(std::bad_alloc,
|
||||
std::runtime_error);
|
||||
/**
|
||||
* Look up key name.
|
||||
*/
|
||||
std::string name() throw(std::bad_alloc);
|
||||
/**
|
||||
* Keyboard key listener.
|
||||
*/
|
||||
struct key_listener
|
||||
{
|
||||
/**
|
||||
* Invoked on key.
|
||||
*/
|
||||
virtual void key_event(const modifier_set& modifiers, keygroup& keygroup, unsigned subkey,
|
||||
bool polarity, const std::string& name) = 0;
|
||||
};
|
||||
/**
|
||||
* Add key listener.
|
||||
*/
|
||||
void add_key_listener(key_listener& l) throw(std::bad_alloc);
|
||||
/**
|
||||
* Remove key listener.
|
||||
*/
|
||||
void remove_key_listener(key_listener& l) throw(std::bad_alloc);
|
||||
/**
|
||||
* Excelusive key listener.
|
||||
*/
|
||||
static void set_exclusive_key_listener(key_listener* l) throw();
|
||||
private:
|
||||
unsigned state;
|
||||
enum type ktype;
|
||||
short cal_left;
|
||||
short cal_center;
|
||||
short cal_right;
|
||||
double cal_tolerance;
|
||||
double compensate(short value);
|
||||
double compensate2(double value);
|
||||
void run_listeners(const modifier_set& modifiers, unsigned subkey, bool polarity, bool really, double x);
|
||||
std::list<key_listener*> listeners;
|
||||
std::string keyname;
|
||||
static key_listener* exclusive;
|
||||
};
|
||||
|
||||
/**
|
||||
* This class handles internals of mapping events from keyboard buttons and pseudo-buttons.
|
||||
*
|
||||
*/
|
||||
template<class T>
|
||||
class keymapper
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \brief Bind a key.
|
||||
*
|
||||
* Binds a key, erroring out if binding would conflict with existing one.
|
||||
*
|
||||
* \param mod Modifier set to require to be pressed.
|
||||
* \param modmask Modifier set to take into account.
|
||||
* \param keyname Key to bind the action to.
|
||||
* \param command The command to bind.
|
||||
* \throws std::bad_alloc Not enough memory.
|
||||
* \throws std::runtime_error The binding would conflict with existing one.
|
||||
* parameter mod: Modifier set to require to be pressed.
|
||||
* parameter modmask: Modifier set to take into account.
|
||||
* parameter keyname: Key to bind the action to.
|
||||
* parameter command: The command to bind.
|
||||
* throws std::bad_alloc: Not enough memory.
|
||||
* throws std::runtime_error: The binding would conflict with existing one or invalid modifier/key.
|
||||
*/
|
||||
void bind(std::string mod, std::string modmask, std::string keyname, std::string command) throw(std::bad_alloc,
|
||||
std::runtime_error)
|
||||
{
|
||||
unsigned _mod = T::mod_str(mod);
|
||||
unsigned _modmask = T::mod_str(modmask);
|
||||
if(_mod & ~_modmask)
|
||||
throw std::runtime_error("Mod must be subset of modmask");
|
||||
typename T::internal_keysymbol _keyname = T::key_str(keyname);
|
||||
/* Check for collisions. */
|
||||
for(auto i = bindings.begin(); i != bindings.end(); i++) {
|
||||
if(!(_keyname == i->symbol))
|
||||
continue;
|
||||
if((_mod & _modmask & i->modmask) != (i->mod & _modmask & i->modmask))
|
||||
continue;
|
||||
throw std::runtime_error("Would conflict with " + T::name_key(i->mod, i->modmask, i->symbol));
|
||||
}
|
||||
struct kdata k;
|
||||
k.mod = _mod;
|
||||
k.modmask = _modmask;
|
||||
k.symbol = _keyname;
|
||||
k.command = command;
|
||||
bindings.push_back(k);
|
||||
}
|
||||
|
||||
static void bind(std::string mod, std::string modmask, std::string keyname, std::string command)
|
||||
throw(std::bad_alloc, std::runtime_error);
|
||||
/**
|
||||
* \brief Unbind a key.
|
||||
*
|
||||
* Unbinds a key, erroring out if binding does not exist..
|
||||
*
|
||||
* \param mod Modifier set to require to be pressed.
|
||||
* \param modmask Modifier set to take into account.
|
||||
* \param keyname Key to bind the action to.
|
||||
* \throws std::bad_alloc Not enough memory.
|
||||
* \throws std::runtime_error The binding does not exist.
|
||||
* parameter mod: Modifier set to require to be pressed.
|
||||
* parameter modmask: Modifier set to take into account.
|
||||
* parameter keyname: Key to bind the action to.
|
||||
* throws std::bad_alloc: Not enough memory.
|
||||
* throws std::runtime_error: The binding does not exist.
|
||||
*/
|
||||
void unbind(std::string mod, std::string modmask, std::string keyname) throw(std::bad_alloc,
|
||||
std::runtime_error)
|
||||
{
|
||||
unsigned _mod = T::mod_str(mod);
|
||||
unsigned _modmask = T::mod_str(modmask);
|
||||
typename T::internal_keysymbol _keyname = T::key_str(keyname);
|
||||
for(auto i = bindings.begin(); i != bindings.end(); i++) {
|
||||
if(!(_keyname == i->symbol) || _mod != i->mod || _modmask != i->modmask)
|
||||
continue;
|
||||
bindings.erase(i);
|
||||
return;
|
||||
}
|
||||
throw std::runtime_error("No such binding");
|
||||
}
|
||||
static void unbind(std::string mod, std::string modmask, std::string keyname) throw(std::bad_alloc,
|
||||
std::runtime_error);
|
||||
|
||||
/**
|
||||
* \brief Map key symbol from keyboard + polarity into a command.
|
||||
* Dump list of bindigns as message to console.
|
||||
*
|
||||
* Takes in symbol from keyboard and polarity. Outputs command to run.
|
||||
*
|
||||
* \param sym Symbol from keyboard (with its mods).
|
||||
* \param polarity True if key is being pressed, false if being released.
|
||||
* \return The command to run. "" if none.
|
||||
* \throws std::bad_alloc Not enough memory.
|
||||
* throws std::bad_alloc: Not enough memory.
|
||||
*/
|
||||
std::string map(typename T::keysymbol sym, bool polarity) throw(std::bad_alloc)
|
||||
{
|
||||
unsigned _mod = T::mod_key(sym);
|
||||
typename T::internal_keysymbol _keyname = T::key_key(sym);
|
||||
for(auto i = bindings.begin(); i != bindings.end(); i++) {
|
||||
if((!(_keyname == i->symbol)) || ((_mod & i->modmask) != (i->mod & i->modmask)))
|
||||
continue;
|
||||
std::string x = fixup_command_polarity(i->command, polarity);
|
||||
if(x == "")
|
||||
continue;
|
||||
return x;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Dump list of bindigns as messages to specified graphics handle.
|
||||
*
|
||||
* \throws std::bad_alloc Not enough memory.
|
||||
*/
|
||||
void dumpbindings() throw(std::bad_alloc)
|
||||
{
|
||||
for(auto i = bindings.begin(); i != bindings.end(); i++)
|
||||
messages << "bind " << T::name_key(i->mod, i->modmask, i->symbol) << " " << i->command
|
||||
<< std::endl;
|
||||
}
|
||||
private:
|
||||
struct kdata
|
||||
{
|
||||
unsigned mod;
|
||||
unsigned modmask;
|
||||
typename T::internal_keysymbol symbol;
|
||||
std::string command;
|
||||
};
|
||||
std::list<kdata> bindings;
|
||||
static void dumpbindings() throw(std::bad_alloc);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
|
82
manual.lyx
82
manual.lyx
|
@ -1666,6 +1666,88 @@ scroll-down
|
|||
Scroll messages window forward one screenful.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsubsection
|
||||
axismode <axis> <mode>
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Set joystick axis mode.
|
||||
<axis> is of form
|
||||
\begin_inset Quotes eld
|
||||
\end_inset
|
||||
|
||||
joystick<num>axis<axis>
|
||||
\begin_inset Quotes erd
|
||||
\end_inset
|
||||
|
||||
, e.g.
|
||||
|
||||
\begin_inset Quotes eld
|
||||
\end_inset
|
||||
|
||||
joystick0axis5
|
||||
\begin_inset Quotes erd
|
||||
\end_inset
|
||||
|
||||
.
|
||||
Mode is one of:
|
||||
\end_layout
|
||||
|
||||
\begin_layout Itemize
|
||||
axis: Normal axis mode
|
||||
\end_layout
|
||||
|
||||
\begin_layout Itemize
|
||||
axis_inverse: Inverse axis mode.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Itemize
|
||||
pressure_0m: Pressure sensitive.
|
||||
Released at 0, pressed at full negative.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Itemize
|
||||
pressure_0p: Pressure sensitive.
|
||||
Released at 0, pressed at full positive.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Itemize
|
||||
pressure_m0: Pressure sensitive.
|
||||
Released at full negative, pressed at 0.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Itemize
|
||||
pressure_mp: Pressure sensitive.
|
||||
Released at full negative, pressed at full positive.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Itemize
|
||||
pressure_p0: Pressure sensitive.
|
||||
Released at full positive, pressed at 0.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Itemize
|
||||
pressure_pm: Pressure sensitive.
|
||||
Released at full positive, pressed at full negative.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Note that if you have Wired Microsoft XBox 360 controller (works great in
|
||||
Linux, BTW), you might want to do the following:
|
||||
\end_layout
|
||||
|
||||
\begin_layout Itemize
|
||||
axismode joystick0axis2 pressure_mp
|
||||
\end_layout
|
||||
|
||||
\begin_layout Itemize
|
||||
axismode joystick0axis5 pressure_mp
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Since axes 2 and 5 are LT/RT, which are pressure sensitive.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsection
|
||||
Settings
|
||||
\end_layout
|
||||
|
|
1289
window-sdl.cpp
1289
window-sdl.cpp
File diff suppressed because it is too large
Load diff
32
window.hpp
32
window.hpp
|
@ -67,38 +67,6 @@ public:
|
|||
*/
|
||||
static void fatal_error() throw();
|
||||
|
||||
/**
|
||||
* Bind a key.
|
||||
*
|
||||
* parameter mod: Set of modifiers.
|
||||
* parameter modmask: Modifier mask (set of modifiers).
|
||||
* parameter keyname: Name of key or pseudo-key.
|
||||
* parameter command: Command to run.
|
||||
* throws std::bad_alloc: Not enough memory.
|
||||
* throws std::runtime_error: Invalid key or modifier name, or conflict.
|
||||
*/
|
||||
static void bind(std::string mod, std::string modmask, std::string keyname, std::string command)
|
||||
throw(std::bad_alloc, std::runtime_error);
|
||||
|
||||
/**
|
||||
* Unbind a key.
|
||||
*
|
||||
* parameter mod: Set of modifiers.
|
||||
* parameter modmask: Modifier mask (set of modifiers).
|
||||
* parameter keyname: Name of key or pseudo-key.
|
||||
* throws std::bad_alloc: Not enough memory.
|
||||
* throws std::runtime_error: Invalid key or modifier name, or not bound.
|
||||
*/
|
||||
static void unbind(std::string mod, std::string modmask, std::string keyname) throw(std::bad_alloc,
|
||||
std::runtime_error);
|
||||
|
||||
/**
|
||||
* Dump bindings into this window.
|
||||
*
|
||||
* throws std::bad_alloc: Not enough memory.
|
||||
*/
|
||||
static void dumpbindings() throw(std::bad_alloc);
|
||||
|
||||
/**
|
||||
* Processes inputs. If in non-modal mode (normal mode without pause), this returns quickly. Otherwise it waits
|
||||
* for modal mode to exit.
|
||||
|
|
Loading…
Add table
Reference in a new issue