#ifndef _keymapper__hpp__included__ #define _keymapper__hpp__included__ #include #include #include #include #include #include #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 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 bindings; }; #endif