lsnes/keymapper.hpp
2011-09-15 22:56:33 +03:00

154 lines
5 KiB
C++

#ifndef _keymapper__hpp__included__
#define _keymapper__hpp__included__
#include <string>
#include <sstream>
#include <stdexcept>
#include <list>
#include <set>
#include <iostream>
#include "window.hpp"
#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.
*/
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.
*/
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.
*/
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);
}
/**
* \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.
*/
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");
}
/**
* \brief Map key symbol from keyboard + polarity into a command.
*
* 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.
*/
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.
*
* \param win The graphics system handle.
* \throws std::bad_alloc Not enough memory.
*/
void dumpbindings(window* win) throw(std::bad_alloc)
{
for(auto i = bindings.begin(); i != bindings.end(); i++)
out(win) << "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;
};
#endif