bindinds: add support for alt/ctrl/shift modifiers and escape key

This commit is contained in:
Andrzej Rybczak
2015-05-12 22:02:09 +02:00
parent e7b152938b
commit dcac42748a
10 changed files with 434 additions and 296 deletions

1
NEWS
View File

@@ -21,6 +21,7 @@ ncmpcpp-0.7 (????-??-??)
* Shuffle function now shuffles only selected range if selection in playlist is active. * Shuffle function now shuffles only selected range if selection in playlist is active.
* NCurses terminal sequence escaping is no longer used as it's not accurate enough. * NCurses terminal sequence escaping is no longer used as it's not accurate enough.
* Selecting items no longer depends on space mode and is bound by default to Insert key. * Selecting items no longer depends on space mode and is bound by default to Insert key.
* Support for Alt, Ctrl and Shift modifiers as well as Escape key was added.
ncmpcpp-0.6.4 (2015-05-02) ncmpcpp-0.6.4 (2015-05-02)

View File

@@ -51,9 +51,11 @@
## picked by ncmpcpp upon next call to readKey function. ## picked by ncmpcpp upon next call to readKey function.
## Accepted values: mouse, up, down, page_up, page_down, ## Accepted values: mouse, up, down, page_up, page_down,
## home, end, space, enter, insert, delete, left, right, ## home, end, space, enter, insert, delete, left, right,
## tab, shift_tab, ctrl_a, ctrl_b, ..., ctrl_z, ctrl_[, ## tab, ctrl_a, ctrl_b, ..., ctrl_z, ctrl_[, ctrl_\\,
## ctrl_\\, ctrl_], ctrl_^, ctrl__, f1, f2, ..., f12, ## ctrl_], ctrl_^, ctrl__, f1, f2, ..., f12, backspace.
## backspace. ## In addition, most of these names can be prefixed with
## alt_/ctrl_/shift_ to be recognized with the appropriate
## modifier key(s).
## ##
## - push_characters "string" - pushes given string into ## - push_characters "string" - pushes given string into
## input queue. ## input queue.

View File

@@ -2626,7 +2626,7 @@ void seek()
? (Timer-t).total_seconds()/2+Config.seek_time ? (Timer-t).total_seconds()/2+Config.seek_time
: Config.seek_time; : Config.seek_time;
Key input = Key::read(*wFooter); NC::Key::Type input = readKey(*wFooter);
auto k = Bindings.get(input); auto k = Bindings.get(input);
if (k.first == k.second || !k.first->isSingle()) // no single action? if (k.first == k.second || !k.first->isSingle()) // no single action?
break; break;

View File

@@ -28,77 +28,83 @@
BindingsConfiguration Bindings; BindingsConfiguration Bindings;
Key Key::noOp = Key(ERR, NCurses);
namespace { namespace {
Key stringToSpecialKey(const std::string &s) NC::Key::Type stringToKey(const std::string &s);
NC::Key::Type stringToSpecialKey(const std::string &s)
{ {
Key result = Key::noOp; NC::Key::Type result = NC::Key::None;
if (!s.compare("mouse")) if (!s.compare(0, 5, "ctrl_") && s.length() == 6)
result = Key(NC::Key::Mouse, Key::NCurses);
else if (!s.compare("up"))
result = Key(NC::Key::Up, Key::NCurses);
else if (!s.compare("down"))
result = Key(NC::Key::Down, Key::NCurses);
else if (!s.compare("page_up"))
result = Key(NC::Key::PageUp, Key::NCurses);
else if (!s.compare("page_down"))
result = Key(NC::Key::PageDown, Key::NCurses);
else if (!s.compare("home"))
result = Key(NC::Key::Home, Key::NCurses);
else if (!s.compare("end"))
result = Key(NC::Key::End, Key::NCurses);
else if (!s.compare("space"))
result = Key(NC::Key::Space, Key::Standard);
else if (!s.compare("enter"))
result = Key(NC::Key::Enter, Key::Standard);
else if (!s.compare("insert"))
result = Key(NC::Key::Insert, Key::NCurses);
else if (!s.compare("delete"))
result = Key(NC::Key::Delete, Key::NCurses);
else if (!s.compare("left"))
result = Key(NC::Key::Left, Key::NCurses);
else if (!s.compare("right"))
result = Key(NC::Key::Right, Key::NCurses);
else if (!s.compare("tab"))
result = Key(NC::Key::Tab, Key::Standard);
else if (!s.compare("shift_tab"))
result = Key(NC::Key::Shift | NC::Key::Tab, Key::NCurses);
else if (!s.compare(0, 5, "ctrl_") && s.length() > 5)
{ {
if (s[5] >= 'a' && s[5] <= 'z') if (s[5] >= 'a' && s[5] <= 'z')
result = Key(NC::Key::Ctrl_A + (s[5] - 'a'), Key::Standard); result = NC::Key::Ctrl_A + (s[5] - 'a');
else if (s[5] == '[') else if (s[5] == '[')
result = Key(NC::Key::Ctrl_LeftBracket, Key::Standard); result = NC::Key::Ctrl_LeftBracket;
else if (s[5] == '\\') else if (s[5] == '\\')
result = Key(NC::Key::Ctrl_Backslash, Key::Standard); result = NC::Key::Ctrl_Backslash;
else if (s[5] == ']') else if (s[5] == ']')
result = Key(NC::Key::Ctrl_RightBracket, Key::Standard); result = NC::Key::Ctrl_RightBracket;
else if (s[5] == '^') else if (s[5] == '^')
result = Key(NC::Key::Ctrl_Caret, Key::Standard); result = NC::Key::Ctrl_Caret;
else if (s[5] == '_') else if (s[5] == '_')
result = Key(NC::Key::Ctrl_Underscore, Key::Standard); result = NC::Key::Ctrl_Underscore;
} }
else if (s.length() > 1 && s[0] == 'f') else if (!s.compare(0, 4, "alt_"))
result = NC::Key::Alt | stringToKey(s.substr(4));
else if (!s.compare(0, 5, "ctrl_"))
result = NC::Key::Ctrl | stringToKey(s.substr(5));
else if (!s.compare(0, 6, "shift_"))
result = NC::Key::Shift | stringToKey(s.substr(6));
else if (!s.compare("escape"))
result = NC::Key::Escape;
else if (!s.compare("mouse"))
result = NC::Key::Mouse;
else if (!s.compare("up"))
result = NC::Key::Up;
else if (!s.compare("down"))
result = NC::Key::Down;
else if (!s.compare("page_up"))
result = NC::Key::PageUp;
else if (!s.compare("page_down"))
result = NC::Key::PageDown;
else if (!s.compare("home"))
result = NC::Key::Home;
else if (!s.compare("end"))
result = NC::Key::End;
else if (!s.compare("space"))
result = NC::Key::Space;
else if (!s.compare("enter"))
result = NC::Key::Enter;
else if (!s.compare("insert"))
result = NC::Key::Insert;
else if (!s.compare("delete"))
result = NC::Key::Delete;
else if (!s.compare("left"))
result = NC::Key::Left;
else if (!s.compare("right"))
result = NC::Key::Right;
else if (!s.compare("tab"))
result = NC::Key::Tab;
else if ((s.length() == 2 || s.length() == 3) && s[0] == 'f')
{ {
int n = atoi(s.c_str() + 1); int n = atoi(s.c_str() + 1);
if (n >= 1 && n <= 12) if (n >= 1 && n <= 12)
result = Key(NC::Key::F1 + n - 1, Key::NCurses); result = NC::Key::F1 + n - 1;
} }
else if (!s.compare("backspace")) else if (!s.compare("backspace"))
result = Key(NC::Key::Backspace, Key::Standard); result = NC::Key::Backspace;
return result; return result;
} }
Key stringToKey(const std::string &s) NC::Key::Type stringToKey(const std::string &s)
{ {
Key result = stringToSpecialKey(s); NC::Key::Type result = stringToSpecialKey(s);
if (result == Key::noOp) if (result == NC::Key::None)
{ {
std::wstring ws = ToWString(s); std::wstring ws = ToWString(s);
if (ws.length() == 1) if (ws.length() == 1)
result = Key(ws[0], Key::Standard); result = ws[0];
} }
return result; return result;
} }
@@ -118,9 +124,9 @@ Actions::BaseAction *parseActionLine(const std::string &line, F error)
{ {
// push single character into input queue // push single character into input queue
std::string arg = getEnclosedString(line, '"', '"', 0); std::string arg = getEnclosedString(line, '"', '"', 0);
Key k = stringToSpecialKey(arg); NC::Key::Type k = stringToSpecialKey(arg);
auto queue = std::vector<int>{ k.getChar() }; auto queue = std::vector<NC::Key::Type>{ k };
if (k != Key::noOp) if (k != NC::Key::None)
result = new Actions::PushCharacters(&Global::wFooter, std::move(queue)); result = new Actions::PushCharacters(&Global::wFooter, std::move(queue));
else else
error() << "invalid character passed to push_character: '" << arg << "'\n"; error() << "invalid character passed to push_character: '" << arg << "'\n";
@@ -131,7 +137,7 @@ Actions::BaseAction *parseActionLine(const std::string &line, F error)
std::string arg = getEnclosedString(line, '"', '"', 0); std::string arg = getEnclosedString(line, '"', '"', 0);
if (!arg.empty()) if (!arg.empty())
{ {
std::vector<int> queue(arg.begin(), arg.end()); std::vector<NC::Key::Type> queue(arg.begin(), arg.end());
// if char is signed, erase 1s from char -> int conversion // if char is signed, erase 1s from char -> int conversion
for (auto it = arg.begin(); it != arg.end(); ++it) for (auto it = arg.begin(); it != arg.end(); ++it)
*it &= 0xff; *it &= 0xff;
@@ -174,19 +180,26 @@ Actions::BaseAction *parseActionLine(const std::string &line, F error)
} }
Key Key::read(NC::Window &w) NC::Key::Type readKey(NC::Window &w)
{ {
Key result = noOp; NC::Key::Type result = NC::Key::None;
std::string tmp; std::string tmp;
int input; NC::Key::Type input;
bool alt_pressed = false;
while (true) while (true)
{ {
input = w.readKey(); input = w.readKey();
if (input == ERR) if (input == NC::Key::None)
break; break;
if (input > 255) if (input & NC::Key::Alt)
{ {
result = Key(input, NCurses); // Complete the key and reapply the mask at the end.
alt_pressed = true;
input &= ~NC::Key::Alt;
}
if (input > 255) // NC special character
{
result = input;
break; break;
} }
else else
@@ -200,11 +213,93 @@ Key Key::read(NC::Window &w)
break; break;
else // character complete else // character complete
{ {
result = Key(wc, Standard); result = wc;
break; break;
} }
} }
} }
if (alt_pressed)
result |= NC::Key::Alt;
return result;
}
std::wstring keyToWString(const NC::Key::Type key)
{
std::wstring result;
if (key == NC::Key::Tab)
result += L"Tab";
else if (key == NC::Key::Enter)
result += L"Enter";
else if (key == NC::Key::Escape)
result += L"Escape";
else if (key >= NC::Key::Ctrl_A && key <= NC::Key::Ctrl_Z)
{
result += L"Ctrl-";
result += 'A' + (key - NC::Key::Ctrl_A);
}
else if (key == NC::Key::Ctrl_LeftBracket)
result += L"Ctrl-[";
else if (key == NC::Key::Ctrl_Backslash)
result += L"Ctrl-\\";
else if (key == NC::Key::Ctrl_RightBracket)
result += L"Ctrl-]";
else if (key == NC::Key::Ctrl_Caret)
result += L"Ctrl-^";
else if (key == NC::Key::Ctrl_Underscore)
result += L"Ctrl-_";
else if (key & NC::Key::Alt)
{
result += L"Alt-";
result += keyToWString(key & ~NC::Key::Alt);
}
else if (key & NC::Key::Ctrl)
{
result += L"Ctrl-";
result += keyToWString(key & ~NC::Key::Ctrl);
}
else if (key & NC::Key::Shift)
{
result += L"Shift-";
result += keyToWString(key & ~NC::Key::Shift);
}
else if (key == NC::Key::Space)
result += L"Space";
else if (key == NC::Key::Backspace)
result += L"Backspace";
else if (key == NC::Key::Insert)
result += L"Insert";
else if (key == NC::Key::Delete)
result += L"Delete";
else if (key == NC::Key::Home)
result += L"Home";
else if (key == NC::Key::End)
result += L"End";
else if (key == NC::Key::PageUp)
result += L"PageUp";
else if (key == NC::Key::PageDown)
result += L"PageDown";
else if (key == NC::Key::Up)
result += L"Up";
else if (key == NC::Key::Down)
result += L"Down";
else if (key == NC::Key::Left)
result += L"Left";
else if (key == NC::Key::Right)
result += L"Right";
else if (key >= NC::Key::F1 && key <= NC::Key::F9)
{
result += L"F";
result += '1' + (key - NC::Key::F1);
}
else if (key >= NC::Key::F10 && key <= NC::Key::F12)
{
result += L"F1";
result += '0' + (key - NC::Key::F10);
}
else
result += std::wstring(1, key);
return result; return result;
} }
@@ -225,7 +320,7 @@ bool BindingsConfiguration::read(const std::string &file)
Binding::ActionChain actions; Binding::ActionChain actions;
// def_key specific variables // def_key specific variables
Key key = Key::noOp; NC::Key::Type key = NC::Key::None;
std::string strkey; std::string strkey;
// def_command specific variables // def_command specific variables
@@ -316,7 +411,7 @@ bool BindingsConfiguration::read(const std::string &file)
in_progress = InProgress::Key; in_progress = InProgress::Key;
strkey = getEnclosedString(line, '"', '"', 0); strkey = getEnclosedString(line, '"', '"', 0);
key = stringToKey(strkey); key = stringToKey(strkey);
if (key == Key::noOp) if (key == NC::Key::None)
{ {
error() << "invalid key: '" << strkey << "'\n"; error() << "invalid key: '" << strkey << "'\n";
break; break;
@@ -347,7 +442,7 @@ bool BindingsConfiguration::read(const std::string &file)
void BindingsConfiguration::generateDefaults() void BindingsConfiguration::generateDefaults()
{ {
Key k = Key::noOp; NC::Key::Type k = NC::Key::None;
if (notBound(k = stringToKey("mouse"))) if (notBound(k = stringToKey("mouse")))
bind(k, Actions::Type::MouseEvent); bind(k, Actions::Type::MouseEvent);
if (notBound(k = stringToKey("up"))) if (notBound(k = stringToKey("up")))
@@ -608,7 +703,7 @@ const Command *BindingsConfiguration::findCommand(const std::string &name)
return ptr; return ptr;
} }
BindingsConfiguration::BindingIteratorPair BindingsConfiguration::get(const Key &k) BindingsConfiguration::BindingIteratorPair BindingsConfiguration::get(const NC::Key::Type &k)
{ {
std::pair<BindingIterator, BindingIterator> result; std::pair<BindingIterator, BindingIterator> result;
auto it = m_bindings.find(k); auto it = m_bindings.find(k);

View File

@@ -27,29 +27,8 @@
#include "actions.h" #include "actions.h"
#include "macro_utilities.h" #include "macro_utilities.h"
/// Key for binding actions to it. Supports non-ascii characters. NC::Key::Type readKey(NC::Window &w);
struct Key std::wstring keyToWString(const NC::Key::Type key);
{
enum Type { Standard, NCurses };
Key(wchar_t ch, Type ct) : m_impl(ch, ct) { }
wchar_t getChar() const { return std::get<0>(m_impl); }
Type getType() const { return std::get<1>(m_impl); }
bool operator< (const Key &rhs) const { return m_impl < rhs.m_impl; }
bool operator<=(const Key &rhs) const { return m_impl <= rhs.m_impl; }
bool operator> (const Key &rhs) const { return m_impl > rhs.m_impl; }
bool operator>=(const Key &rhs) const { return m_impl >= rhs.m_impl; }
bool operator==(const Key &rhs) const { return m_impl == rhs.m_impl; }
bool operator!=(const Key &rhs) const { return m_impl != rhs.m_impl; }
static Key read(NC::Window &w);
static Key noOp;
private:
std::tuple<wchar_t, Type> m_impl;
};
/// Represents either single action or chain of actions bound to a certain key /// Represents either single action or chain of actions bound to a certain key
struct Binding struct Binding
@@ -100,13 +79,8 @@ private:
/// Keybindings configuration /// Keybindings configuration
class BindingsConfiguration class BindingsConfiguration
{ {
struct KeyHash {
size_t operator()(const Key &k) const {
return (k.getChar() << 1) | (k.getType() == Key::Standard);
}
};
typedef std::unordered_map<std::string, Command> CommandsSet; typedef std::unordered_map<std::string, Command> CommandsSet;
typedef std::unordered_map<Key, std::vector<Binding>, KeyHash> BindingsMap; typedef std::unordered_map<NC::Key::Type, std::vector<Binding>> BindingsMap;
public: public:
typedef BindingsMap::value_type::second_type::iterator BindingIterator; typedef BindingsMap::value_type::second_type::iterator BindingIterator;
@@ -117,18 +91,18 @@ public:
void generateDefaults(); void generateDefaults();
const Command *findCommand(const std::string &name); const Command *findCommand(const std::string &name);
BindingIteratorPair get(const Key &k); BindingIteratorPair get(const NC::Key::Type &k);
BindingsMap::const_iterator begin() const { return m_bindings.begin(); } BindingsMap::const_iterator begin() const { return m_bindings.begin(); }
BindingsMap::const_iterator end() const { return m_bindings.end(); } BindingsMap::const_iterator end() const { return m_bindings.end(); }
private: private:
bool notBound(const Key &k) const { bool notBound(const NC::Key::Type &k) const {
return k != Key::noOp && m_bindings.find(k) == m_bindings.end(); return k != NC::Key::None && m_bindings.find(k) == m_bindings.end();
} }
template <typename ArgT> template <typename ArgT>
void bind(Key k, ArgT &&t) { void bind(NC::Key::Type k, ArgT &&t) {
m_bindings[k].push_back(std::forward<ArgT>(t)); m_bindings[k].push_back(std::forward<ArgT>(t));
} }

View File

@@ -36,74 +36,36 @@ Help *myHelp;
namespace { namespace {
std::string key_to_string(const Key &key)
{
std::string result;
if (key == Key(NC::Key::Up, Key::NCurses))
result += "Up";
else if (key == Key(NC::Key::Down, Key::NCurses))
result += "Down";
else if (key == Key(NC::Key::PageUp, Key::NCurses))
result += "Page Up";
else if (key == Key(NC::Key::PageDown, Key::NCurses))
result += "Page Down";
else if (key == Key(NC::Key::Home, Key::NCurses))
result += "Home";
else if (key == Key(NC::Key::End, Key::NCurses))
result += "End";
else if (key == Key(NC::Key::Space, Key::Standard))
result += "Space";
else if (key == Key(NC::Key::Enter, Key::Standard))
result += "Enter";
else if (key == Key(NC::Key::Insert, Key::NCurses))
result += "Insert";
else if (key == Key(NC::Key::Delete, Key::NCurses))
result += "Delete";
else if (key == Key(NC::Key::Right, Key::NCurses))
result += "Right";
else if (key == Key(NC::Key::Left, Key::NCurses))
result += "Left";
else if (key == Key(NC::Key::Tab, Key::Standard))
result += "Tab";
else if (key == Key(NC::Key::Shift | NC::Key::Tab, Key::NCurses))
result += "Shift-Tab";
else if (key >= Key(NC::Key::Ctrl_A, Key::Standard) && key <= Key(NC::Key::Ctrl_Z, Key::Standard))
{
result += "Ctrl-";
result += key.getChar()+64;
}
else if (key >= Key(NC::Key::F1, Key::NCurses) && key <= Key(NC::Key::F12, Key::NCurses))
{
result += "F";
result += boost::lexical_cast<std::string>(key.getChar()-NC::Key::F1+1);
}
else if (key == Key(NC::Key::Backspace, Key::Standard))
result += "Backspace";
else
result += ToString(std::wstring(1, key.getChar()));
return result;
}
std::string display_keys(const Actions::Type at) std::string display_keys(const Actions::Type at)
{ {
std::string result, skey; std::wstring result, skey;
for (auto it = Bindings.begin(); it != Bindings.end(); ++it) for (auto it = Bindings.begin(); it != Bindings.end(); ++it)
{ {
for (auto j = it->second.begin(); j != it->second.end(); ++j) for (auto j = it->second.begin(); j != it->second.end(); ++j)
{ {
if (j->isSingle() && j->action()->type() == at) if (j->isSingle() && j->action()->type() == at)
{ {
skey = key_to_string(it->first); skey = keyToWString(it->first);
if (!skey.empty()) if (!skey.empty())
{ {
result += std::move(skey); result += std::move(skey);
result += " "; result += ' ';
} }
} }
} }
} }
result.resize(16, ' '); size_t i = 0, len = 0;
return result; const size_t max_len = 20;
for (; i < result.size(); ++i)
{
int width = std::max(1, wcwidth(result[i]));
if (len+width > max_len)
break;
else
len += width;
}
result.resize(i + max_len - len, ' ');
return ToString(result);
} }
void section(NC::Scrollpad &w, const char *type_, const char *title_) void section(NC::Scrollpad &w, const char *type_, const char *title_)

View File

@@ -29,14 +29,14 @@ namespace Actions {
struct PushCharacters: BaseAction struct PushCharacters: BaseAction
{ {
PushCharacters(NC::Window **w, std::vector<int> &&queue) PushCharacters(NC::Window **w, std::vector<NC::Key::Type> &&queue)
: BaseAction(Type::MacroUtility, ""), m_window(w), m_queue(queue) { } : BaseAction(Type::MacroUtility, ""), m_window(w), m_queue(queue) { }
private: private:
virtual void run() OVERRIDE; virtual void run() OVERRIDE;
NC::Window **m_window; NC::Window **m_window;
std::vector<int> m_queue; std::vector<NC::Key::Type> m_queue;
}; };
struct RequireRunnable: BaseAction struct RequireRunnable: BaseAction

View File

@@ -160,7 +160,7 @@ int main(int argc, char **argv)
// local variables // local variables
bool key_pressed = false; bool key_pressed = false;
Key input = Key::noOp; auto input = NC::Key::None;
auto connect_attempt = boost::posix_time::from_time_t(0); auto connect_attempt = boost::posix_time::from_time_t(0);
auto past = boost::posix_time::from_time_t(0); auto past = boost::posix_time::from_time_t(0);
@@ -210,12 +210,14 @@ int main(int argc, char **argv)
if (key_pressed) if (key_pressed)
myScreen->refreshWindow(); myScreen->refreshWindow();
input = Key::read(*wFooter); input = readKey(*wFooter);
key_pressed = input != Key::noOp; key_pressed = input != NC::Key::None;
if (!key_pressed) if (!key_pressed)
continue; continue;
Statusbar::print(ToString(keyToWString(input)));
// The reason we want to update timer here is that if the timer is updated // The reason we want to update timer here is that if the timer is updated
// in Status::trace, then Key::read usually blocks for 500ms and if key is // in Status::trace, then Key::read usually blocks for 500ms and if key is
// pressed 400ms after Key::read was called, we end up with Timer that is // pressed 400ms after Key::read was called, we end up with Timer that is

View File

@@ -723,9 +723,8 @@ bool Window::FDCallbacksListEmpty() const
return m_fds.empty(); return m_fds.empty();
} }
int Window::getInputChar() Key::Type Window::getInputChar(int key)
{ {
int key = wgetch(m_window);
if (!m_escape_terminal_sequences || key != Key::Escape) if (!m_escape_terminal_sequences || key != Key::Escape)
return key; return key;
auto define_mouse_event = [this](int type) { auto define_mouse_event = [this](int type) {
@@ -761,6 +760,36 @@ int Window::getInputChar()
return Key::None; return Key::None;
return Key::Mouse; return Key::Mouse;
}; };
auto get_xterm_modifier_key = [](int ch) {
Key::Type modifier;
switch (ch)
{
case '2':
modifier = Key::Shift;
break;
case '3':
modifier = Key::Alt;
break;
case '4':
modifier = Key::Alt | Key::Shift;
break;
case '5':
modifier = Key::Ctrl;
break;
case '6':
modifier = Key::Ctrl | Key::Shift;
break;
case '7':
modifier = Key::Alt | Key::Ctrl;
break;
case '8':
modifier = Key::Alt | Key::Ctrl | Key::Shift;
break;
default:
modifier = Key::None;
}
return modifier;
};
auto parse_number = [this](int &result) { auto parse_number = [this](int &result) {
int x; int x;
while (true) while (true)
@@ -777,31 +806,41 @@ int Window::getInputChar()
{ {
case '\t': // tty case '\t': // tty
return Key::Shift | Key::Tab; return Key::Shift | Key::Tab;
case 'O': // F1 to F4 in xterm case 'O': // ctrl+arrows in rxvt, F1 to F4 in xterm
key = wgetch(m_window); key = wgetch(m_window);
switch (key) switch (key)
{ {
case 'a':
return Key::Ctrl | Key::Up;
case 'b':
return Key::Ctrl | Key::Down;
case 'c':
return Key::Ctrl | Key::Right;
case 'd':
return Key::Ctrl | Key::Left;
case 'P': case 'P':
key = Key::F1; return Key::F1;
break;
case 'Q': case 'Q':
key = Key::F2; return Key::F2;
break;
case 'R': case 'R':
key = Key::F3; return Key::F3;
break;
case 'S': case 'S':
key = Key::F4; return Key::F4;
break;
default: default:
key = Key::None; return Key::None;
break;
} }
return key;
case '[': case '[':
key = wgetch(m_window); key = wgetch(m_window);
switch (key) switch (key)
{ {
case 'a':
return Key::Shift | Key::Up;
case 'b':
return Key::Shift | Key::Down;
case 'c':
return Key::Shift | Key::Right;
case 'd':
return Key::Shift | Key::Left;
case 'A': case 'A':
return Key::Up; return Key::Up;
case 'B': case 'B':
@@ -814,7 +853,7 @@ int Window::getInputChar()
return Key::End; return Key::End;
case 'H': // xterm case 'H': // xterm
return Key::Home; return Key::Home;
case 'M': case 'M': // standard mouse event
{ {
key = wgetch(m_window); key = wgetch(m_window);
int raw_x = wgetch(m_window); int raw_x = wgetch(m_window);
@@ -831,34 +870,78 @@ int Window::getInputChar()
switch (key) switch (key)
{ {
case 'A': case 'A':
key = Key::F1; return Key::F1;
break;
case 'B': case 'B':
key = Key::F2; return Key::F2;
break;
case 'C': case 'C':
key = Key::F3; return Key::F3;
break;
case 'D': case 'D':
key = Key::F4; return Key::F4;
break;
case 'E': case 'E':
key = Key::F5; return Key::F5;
break;
default: default:
key = Key::None; return Key::None;
break;
} }
return key;
case '1': case '2': case '3': case '1': case '2': case '3':
case '4': case '5': case '6': case '4': case '5': case '6':
case '7': case '8': case '9': case '7': case '8': case '9':
{ {
key -= '0'; key -= '0';
int delim = parse_number(key); int delim = parse_number(key);
if (key >= 2 && key <= 8)
{
Key::Type modifier;
switch (delim)
{
case '~':
modifier = Key::Null;
break;
case '^':
modifier = Key::Ctrl;
break;
case '$':
modifier = Key::Shift;
break;
case '@':
modifier = Key::Ctrl | Key::Shift;
break;
case ';': // xterm insert/delete/page up/page down
{
int local_key = wgetch(m_window);
modifier = get_xterm_modifier_key(local_key);
local_key = wgetch(m_window);
if (local_key != '~' || (key != 2 && key != 3 && key != 5 && key != 6))
return Key::None;
break;
}
default:
return Key::None;
}
switch (key)
{
case 2:
return modifier | Key::Insert;
case 3:
return modifier | Key::Delete;
case 4:
return modifier | Key::End;
case 5:
return modifier | Key::PageUp;
case 6:
return modifier | Key::PageDown;
case 7:
return modifier | Key::Home;
case 8:
return modifier | Key::End;
default:
std::cerr << "Unreachable code, aborting.\n";
std::terminate();
}
}
switch (delim) switch (delim)
{ {
case '~': case '~':
{
switch (key) switch (key)
{ {
case 1: // tty case 1: // tty
@@ -879,8 +962,6 @@ int Window::getInputChar()
return Key::F7; return Key::F7;
case 19: case 19:
return Key::F8; return Key::F8;
case 2:
return Key::Insert;
case 20: case 20:
return Key::F9; return Key::F9;
case 21: case 21:
@@ -889,33 +970,51 @@ int Window::getInputChar()
return Key::F11; return Key::F11;
case 24: case 24:
return Key::F12; return Key::F12;
case 3:
return Key::Delete;
case 4:
return Key::End;
case 5:
return Key::PageUp;
case 6:
return Key::PageDown;
case 7:
return Key::Home;
case 8:
return Key::End;
default: default:
return Key::None; return Key::None;
} }
case ';': // urxvt mouse }
m_mouse_event.x = 0; case ';':
delim = parse_number(m_mouse_event.x); switch (key)
if (delim != ';') {
return Key::None; case 1: // xterm
m_mouse_event.y = 0; {
delim = parse_number(m_mouse_event.y); key = wgetch(m_window);
if (delim != 'M') Key::Type modifier = get_xterm_modifier_key(key);
return Key::None; if (modifier == Key::None)
--m_mouse_event.x; return Key::None;
--m_mouse_event.y; key = wgetch(m_window);
return define_mouse_event(key); switch (key)
{
case 'A':
return modifier | Key::Up;
case 'B':
return modifier | Key::Down;
case 'C':
return modifier | Key::Right;
case 'D':
return modifier | Key::Left;
case 'F':
return modifier | Key::End;
case 'H':
return modifier | Key::Home;
default:
return Key::None;
}
}
default: // urxvt mouse
m_mouse_event.x = 0;
delim = parse_number(m_mouse_event.x);
if (delim != ';')
return Key::None;
m_mouse_event.y = 0;
delim = parse_number(m_mouse_event.y);
if (delim != 'M')
return Key::None;
--m_mouse_event.x;
--m_mouse_event.y;
return define_mouse_event(key);
}
default: default:
return Key::None; return Key::None;
} }
@@ -923,22 +1022,21 @@ int Window::getInputChar()
default: default:
return Key::None; return Key::None;
} }
break;
case ERR: case ERR:
return Key::Escape; return Key::Escape;
default: default: // alt + something
// this should always succeed as we just got it {
assert(ungetch(key) == OK); auto key_prim = getInputChar(key);
key = getInputChar(); if (key_prim != Key::None)
if (key != Key::None) return Key::Alt | key_prim;
m_input_queue.push(key); return Key::None;
return Key::Alt; }
} }
} }
int Window::readKey() Key::Type Window::readKey()
{ {
int result; Key::Type result;
// if there are characters in input queue, // if there are characters in input queue,
// get them and return immediately. // get them and return immediately.
if (!m_input_queue.empty()) if (!m_input_queue.empty())
@@ -963,7 +1061,10 @@ int Window::readKey()
if (select(fd_max+1, &fdset, 0, 0, m_window_timeout < 0 ? 0 : &timeout) > 0) if (select(fd_max+1, &fdset, 0, 0, m_window_timeout < 0 ? 0 : &timeout) > 0)
{ {
result = FD_ISSET(STDIN_FILENO, &fdset) ? getInputChar() : Key::None; if (FD_ISSET(STDIN_FILENO, &fdset))
result = getInputChar(wgetch(m_window));
else
result = Key::None;
for (FDCallbacks::const_iterator it = m_fds.begin(); it != m_fds.end(); ++it) for (FDCallbacks::const_iterator it = m_fds.begin(); it != m_fds.end(); ++it)
if (FD_ISSET(it->first, &fdset)) if (FD_ISSET(it->first, &fdset))
@@ -974,7 +1075,7 @@ int Window::readKey()
return result; return result;
} }
void Window::pushChar(int ch) void Window::pushChar(const Key::Type ch)
{ {
m_input_queue.push(ch); m_input_queue.push(ch);
} }

View File

@@ -49,82 +49,83 @@ namespace NC {
namespace Key { namespace Key {
typedef const int Type; typedef uint32_t Type;
Type None = -1; const Type None = -1;
// modifier masks // modifier masks
Type Alt = 1 << 16; const Type Special = 1 << 31;
Type Ctrl = 1 << 17; const Type Alt = 1 << 30;
Type Shift = 1 << 18; const Type Ctrl = 1 << 29;
const Type Shift = 1 << 28;
Type Ctrl_A = 1; // useful names
Type Ctrl_B = 2; const Type Null = 0;
Type Ctrl_C = 3; const Type Space = 32;
Type Ctrl_D = 4; const Type Backspace = 127;
Type Ctrl_E = 5;
Type Ctrl_F = 6;
Type Ctrl_G = 7;
Type Ctrl_H = 8;
Type Ctrl_I = 9;
Type Ctrl_J = 10;
Type Ctrl_K = 11;
Type Ctrl_L = 12;
Type Ctrl_M = 13;
Type Ctrl_N = 14;
Type Ctrl_O = 15;
Type Ctrl_P = 16;
Type Ctrl_Q = 17;
Type Ctrl_R = 18;
Type Ctrl_S = 19;
Type Ctrl_T = 20;
Type Ctrl_U = 21;
Type Ctrl_V = 22;
Type Ctrl_W = 23;
Type Ctrl_X = 24;
Type Ctrl_Y = 25;
Type Ctrl_Z = 26;
Type Ctrl_LeftBracket = 27;
Type Ctrl_Backslash = 28;
Type Ctrl_RightBracket = 29;
Type Ctrl_Caret = 30;
Type Ctrl_Underscore = 31;
Type Space = 32; // ctrl-?
Type Backspace = 127; const Type Ctrl_A = 1;
const Type Ctrl_B = 2;
const Type Ctrl_C = 3;
const Type Ctrl_D = 4;
const Type Ctrl_E = 5;
const Type Ctrl_F = 6;
const Type Ctrl_G = 7;
const Type Ctrl_H = 8;
const Type Ctrl_I = 9;
const Type Ctrl_J = 10;
const Type Ctrl_K = 11;
const Type Ctrl_L = 12;
const Type Ctrl_M = 13;
const Type Ctrl_N = 14;
const Type Ctrl_O = 15;
const Type Ctrl_P = 16;
const Type Ctrl_Q = 17;
const Type Ctrl_R = 18;
const Type Ctrl_S = 19;
const Type Ctrl_T = 20;
const Type Ctrl_U = 21;
const Type Ctrl_V = 22;
const Type Ctrl_W = 23;
const Type Ctrl_X = 24;
const Type Ctrl_Y = 25;
const Type Ctrl_Z = 26;
const Type Ctrl_LeftBracket = 27;
const Type Ctrl_Backslash = 28;
const Type Ctrl_RightBracket = 29;
const Type Ctrl_Caret = 30;
const Type Ctrl_Underscore = 31;
// useful duplicates // useful duplicates
Type Tab = 9; const Type Tab = 9;
Type Enter = 13; const Type Enter = 13;
Type Escape = 27; const Type Escape = 27;
// special values, beyond one byte // special values, beyond one byte
Type Insert = 256; const Type Insert = Special | 256;
Type Delete = 257; const Type Delete = Special | 257;
Type Home = 258; const Type Home = Special | 258;
Type End = 259; const Type End = Special | 259;
Type PageUp = 260; const Type PageUp = Special | 260;
Type PageDown = 261; const Type PageDown = Special | 261;
const Type Up = Special | 262;
Type Up = 262; const Type Down = Special | 263;
Type Down = 263; const Type Left = Special | 264;
Type Left = 264; const Type Right = Special | 265;
Type Right = 265; const Type F1 = Special | 266;
const Type F2 = Special | 267;
Type F1 = 266; const Type F3 = Special | 268;
Type F2 = 267; const Type F4 = Special | 269;
Type F3 = 268; const Type F5 = Special | 270;
Type F4 = 269; const Type F6 = Special | 271;
Type F5 = 270; const Type F7 = Special | 272;
Type F6 = 271; const Type F8 = Special | 273;
Type F7 = 272; const Type F9 = Special | 274;
Type F8 = 273; const Type F10 = Special | 275;
Type F9 = 274; const Type F11 = Special | 276;
Type F10 = 275; const Type F12 = Special | 277;
Type F11 = 276; const Type Mouse = Special | 278;
Type F12 = 277;
Type Mouse = 278;
} }
@@ -404,13 +405,13 @@ struct Window
/// Reads key from standard input (or takes it from input queue) /// Reads key from standard input (or takes it from input queue)
/// and writes it into read_key variable /// and writes it into read_key variable
int readKey(); Key::Type readKey();
/// Push single character into input queue, so it can get consumed by ReadKey /// Push single character into input queue, so it can get consumed by ReadKey
void pushChar(int ch); void pushChar(const NC::Key::Type ch);
/// @return const reference to internal input queue /// @return const reference to internal input queue
const std::queue<int> &inputQueue() { return m_input_queue; } const std::queue<NC::Key::Type> &inputQueue() { return m_input_queue; }
/// Scrolls the window by amount of lines given in its parameter /// Scrolls the window by amount of lines given in its parameter
/// @param where indicates how many lines it has to scroll /// @param where indicates how many lines it has to scroll
@@ -479,7 +480,7 @@ protected:
Border m_border; Border m_border;
private: private:
int getInputChar(); Key::Type getInputChar(int key);
/// Sets state of bold attribute (internal use only) /// Sets state of bold attribute (internal use only)
/// @param bold_state state of bold attribute /// @param bold_state state of bold attribute
@@ -515,7 +516,7 @@ private:
/// input queue of a window. you can put characters there using /// input queue of a window. you can put characters there using
/// PushChar and they will be immediately consumed and /// PushChar and they will be immediately consumed and
/// returned by ReadKey /// returned by ReadKey
std::queue<int> m_input_queue; std::queue<Key::Type> m_input_queue;
/// containter used for additional file descriptors that have /// containter used for additional file descriptors that have
/// to be polled in ReadKey() and correspondent callbacks that /// to be polled in ReadKey() and correspondent callbacks that