From dcac42748a33c2b2e029af6859786caa9aec2ec3 Mon Sep 17 00:00:00 2001 From: Andrzej Rybczak Date: Tue, 12 May 2015 22:02:09 +0200 Subject: [PATCH] bindinds: add support for alt/ctrl/shift modifiers and escape key --- NEWS | 1 + doc/bindings | 8 +- src/actions.cpp | 2 +- src/bindings.cpp | 221 +++++++++++++++++++++++++++------------ src/bindings.h | 40 ++------ src/help.cpp | 68 +++--------- src/macro_utilities.h | 4 +- src/ncmpcpp.cpp | 8 +- src/window.cpp | 233 ++++++++++++++++++++++++++++++------------ src/window.h | 145 +++++++++++++------------- 10 files changed, 434 insertions(+), 296 deletions(-) diff --git a/NEWS b/NEWS index 8d4db6ef..f77ca4db 100644 --- a/NEWS +++ b/NEWS @@ -21,6 +21,7 @@ ncmpcpp-0.7 (????-??-??) * 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. * 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) diff --git a/doc/bindings b/doc/bindings index c4df031b..a0f89c52 100644 --- a/doc/bindings +++ b/doc/bindings @@ -51,9 +51,11 @@ ## picked by ncmpcpp upon next call to readKey function. ## Accepted values: mouse, up, down, page_up, page_down, ## home, end, space, enter, insert, delete, left, right, -## tab, shift_tab, ctrl_a, ctrl_b, ..., ctrl_z, ctrl_[, -## ctrl_\\, ctrl_], ctrl_^, ctrl__, f1, f2, ..., f12, -## backspace. +## tab, ctrl_a, ctrl_b, ..., ctrl_z, ctrl_[, ctrl_\\, +## ctrl_], ctrl_^, ctrl__, f1, f2, ..., f12, 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 ## input queue. diff --git a/src/actions.cpp b/src/actions.cpp index c1f2a796..a2854f4c 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -2626,7 +2626,7 @@ void seek() ? (Timer-t).total_seconds()/2+Config.seek_time : Config.seek_time; - Key input = Key::read(*wFooter); + NC::Key::Type input = readKey(*wFooter); auto k = Bindings.get(input); if (k.first == k.second || !k.first->isSingle()) // no single action? break; diff --git a/src/bindings.cpp b/src/bindings.cpp index 507e5abc..ebc42a32 100644 --- a/src/bindings.cpp +++ b/src/bindings.cpp @@ -28,77 +28,83 @@ BindingsConfiguration Bindings; -Key Key::noOp = Key(ERR, NCurses); - 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; - if (!s.compare("mouse")) - 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) + NC::Key::Type result = NC::Key::None; + if (!s.compare(0, 5, "ctrl_") && s.length() == 6) { 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] == '[') - result = Key(NC::Key::Ctrl_LeftBracket, Key::Standard); + result = NC::Key::Ctrl_LeftBracket; else if (s[5] == '\\') - result = Key(NC::Key::Ctrl_Backslash, Key::Standard); + result = NC::Key::Ctrl_Backslash; else if (s[5] == ']') - result = Key(NC::Key::Ctrl_RightBracket, Key::Standard); + result = NC::Key::Ctrl_RightBracket; else if (s[5] == '^') - result = Key(NC::Key::Ctrl_Caret, Key::Standard); + result = NC::Key::Ctrl_Caret; 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); 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")) - result = Key(NC::Key::Backspace, Key::Standard); + result = NC::Key::Backspace; return result; } -Key stringToKey(const std::string &s) +NC::Key::Type stringToKey(const std::string &s) { - Key result = stringToSpecialKey(s); - if (result == Key::noOp) + NC::Key::Type result = stringToSpecialKey(s); + if (result == NC::Key::None) { std::wstring ws = ToWString(s); if (ws.length() == 1) - result = Key(ws[0], Key::Standard); + result = ws[0]; } return result; } @@ -118,9 +124,9 @@ Actions::BaseAction *parseActionLine(const std::string &line, F error) { // push single character into input queue std::string arg = getEnclosedString(line, '"', '"', 0); - Key k = stringToSpecialKey(arg); - auto queue = std::vector{ k.getChar() }; - if (k != Key::noOp) + NC::Key::Type k = stringToSpecialKey(arg); + auto queue = std::vector{ k }; + if (k != NC::Key::None) result = new Actions::PushCharacters(&Global::wFooter, std::move(queue)); else 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); if (!arg.empty()) { - std::vector queue(arg.begin(), arg.end()); + std::vector queue(arg.begin(), arg.end()); // if char is signed, erase 1s from char -> int conversion for (auto it = arg.begin(); it != arg.end(); ++it) *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; - int input; + NC::Key::Type input; + bool alt_pressed = false; while (true) { input = w.readKey(); - if (input == ERR) + if (input == NC::Key::None) 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; } else @@ -200,11 +213,93 @@ Key Key::read(NC::Window &w) break; else // character complete { - result = Key(wc, Standard); + result = wc; 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; } @@ -225,7 +320,7 @@ bool BindingsConfiguration::read(const std::string &file) Binding::ActionChain actions; // def_key specific variables - Key key = Key::noOp; + NC::Key::Type key = NC::Key::None; std::string strkey; // def_command specific variables @@ -316,7 +411,7 @@ bool BindingsConfiguration::read(const std::string &file) in_progress = InProgress::Key; strkey = getEnclosedString(line, '"', '"', 0); key = stringToKey(strkey); - if (key == Key::noOp) + if (key == NC::Key::None) { error() << "invalid key: '" << strkey << "'\n"; break; @@ -347,7 +442,7 @@ bool BindingsConfiguration::read(const std::string &file) void BindingsConfiguration::generateDefaults() { - Key k = Key::noOp; + NC::Key::Type k = NC::Key::None; if (notBound(k = stringToKey("mouse"))) bind(k, Actions::Type::MouseEvent); if (notBound(k = stringToKey("up"))) @@ -608,7 +703,7 @@ const Command *BindingsConfiguration::findCommand(const std::string &name) return ptr; } -BindingsConfiguration::BindingIteratorPair BindingsConfiguration::get(const Key &k) +BindingsConfiguration::BindingIteratorPair BindingsConfiguration::get(const NC::Key::Type &k) { std::pair result; auto it = m_bindings.find(k); diff --git a/src/bindings.h b/src/bindings.h index 929000be..35d3e3dd 100644 --- a/src/bindings.h +++ b/src/bindings.h @@ -27,29 +27,8 @@ #include "actions.h" #include "macro_utilities.h" -/// Key for binding actions to it. Supports non-ascii characters. -struct 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 m_impl; -}; +NC::Key::Type readKey(NC::Window &w); +std::wstring keyToWString(const NC::Key::Type key); /// Represents either single action or chain of actions bound to a certain key struct Binding @@ -100,13 +79,8 @@ private: /// Keybindings configuration class BindingsConfiguration { - struct KeyHash { - size_t operator()(const Key &k) const { - return (k.getChar() << 1) | (k.getType() == Key::Standard); - } - }; typedef std::unordered_map CommandsSet; - typedef std::unordered_map, KeyHash> BindingsMap; + typedef std::unordered_map> BindingsMap; public: typedef BindingsMap::value_type::second_type::iterator BindingIterator; @@ -117,18 +91,18 @@ public: void generateDefaults(); 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 end() const { return m_bindings.end(); } private: - bool notBound(const Key &k) const { - return k != Key::noOp && m_bindings.find(k) == m_bindings.end(); + bool notBound(const NC::Key::Type &k) const { + return k != NC::Key::None && m_bindings.find(k) == m_bindings.end(); } template - void bind(Key k, ArgT &&t) { + void bind(NC::Key::Type k, ArgT &&t) { m_bindings[k].push_back(std::forward(t)); } diff --git a/src/help.cpp b/src/help.cpp index 1c22a2b3..6e86fd49 100644 --- a/src/help.cpp +++ b/src/help.cpp @@ -36,74 +36,36 @@ Help *myHelp; 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(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 result, skey; + std::wstring result, skey; for (auto it = Bindings.begin(); it != Bindings.end(); ++it) { for (auto j = it->second.begin(); j != it->second.end(); ++j) { if (j->isSingle() && j->action()->type() == at) { - skey = key_to_string(it->first); + skey = keyToWString(it->first); if (!skey.empty()) { result += std::move(skey); - result += " "; + result += ' '; } } } } - result.resize(16, ' '); - return result; + size_t i = 0, len = 0; + 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_) diff --git a/src/macro_utilities.h b/src/macro_utilities.h index 98c5afd6..2f75b00c 100644 --- a/src/macro_utilities.h +++ b/src/macro_utilities.h @@ -29,14 +29,14 @@ namespace Actions { struct PushCharacters: BaseAction { - PushCharacters(NC::Window **w, std::vector &&queue) + PushCharacters(NC::Window **w, std::vector &&queue) : BaseAction(Type::MacroUtility, ""), m_window(w), m_queue(queue) { } private: virtual void run() OVERRIDE; NC::Window **m_window; - std::vector m_queue; + std::vector m_queue; }; struct RequireRunnable: BaseAction diff --git a/src/ncmpcpp.cpp b/src/ncmpcpp.cpp index afd2b247..32c9859c 100644 --- a/src/ncmpcpp.cpp +++ b/src/ncmpcpp.cpp @@ -160,7 +160,7 @@ int main(int argc, char **argv) // local variables bool key_pressed = false; - Key input = Key::noOp; + auto input = NC::Key::None; auto connect_attempt = 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) myScreen->refreshWindow(); - input = Key::read(*wFooter); - key_pressed = input != Key::noOp; + input = readKey(*wFooter); + key_pressed = input != NC::Key::None; if (!key_pressed) continue; + Statusbar::print(ToString(keyToWString(input))); + // 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 // pressed 400ms after Key::read was called, we end up with Timer that is diff --git a/src/window.cpp b/src/window.cpp index 8f994f31..4cb98d73 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -723,9 +723,8 @@ bool Window::FDCallbacksListEmpty() const 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) return key; auto define_mouse_event = [this](int type) { @@ -761,6 +760,36 @@ int Window::getInputChar() return Key::None; 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) { int x; while (true) @@ -777,31 +806,41 @@ int Window::getInputChar() { case '\t': // tty 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); 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': - key = Key::F1; - break; + return Key::F1; case 'Q': - key = Key::F2; - break; + return Key::F2; case 'R': - key = Key::F3; - break; + return Key::F3; case 'S': - key = Key::F4; - break; + return Key::F4; default: - key = Key::None; - break; + return Key::None; } - return key; case '[': key = wgetch(m_window); 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': return Key::Up; case 'B': @@ -814,7 +853,7 @@ int Window::getInputChar() return Key::End; case 'H': // xterm return Key::Home; - case 'M': + case 'M': // standard mouse event { key = wgetch(m_window); int raw_x = wgetch(m_window); @@ -831,34 +870,78 @@ int Window::getInputChar() switch (key) { case 'A': - key = Key::F1; - break; + return Key::F1; case 'B': - key = Key::F2; - break; + return Key::F2; case 'C': - key = Key::F3; - break; + return Key::F3; case 'D': - key = Key::F4; - break; + return Key::F4; case 'E': - key = Key::F5; - break; + return Key::F5; default: - key = Key::None; - break; + return Key::None; } - return key; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { key -= '0'; 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) { case '~': + { switch (key) { case 1: // tty @@ -879,8 +962,6 @@ int Window::getInputChar() return Key::F7; case 19: return Key::F8; - case 2: - return Key::Insert; case 20: return Key::F9; case 21: @@ -889,33 +970,51 @@ int Window::getInputChar() return Key::F11; case 24: 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: return Key::None; } - case ';': // 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); + } + case ';': + switch (key) + { + case 1: // xterm + { + key = wgetch(m_window); + Key::Type modifier = get_xterm_modifier_key(key); + if (modifier == Key::None) + return Key::None; + key = wgetch(m_window); + 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: return Key::None; } @@ -923,22 +1022,21 @@ int Window::getInputChar() default: return Key::None; } - break; case ERR: return Key::Escape; - default: - // this should always succeed as we just got it - assert(ungetch(key) == OK); - key = getInputChar(); - if (key != Key::None) - m_input_queue.push(key); - return Key::Alt; + default: // alt + something + { + auto key_prim = getInputChar(key); + if (key_prim != Key::None) + return Key::Alt | key_prim; + return Key::None; + } } } -int Window::readKey() +Key::Type Window::readKey() { - int result; + Key::Type result; // if there are characters in input queue, // get them and return immediately. 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) { - 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) if (FD_ISSET(it->first, &fdset)) @@ -974,7 +1075,7 @@ int Window::readKey() return result; } -void Window::pushChar(int ch) +void Window::pushChar(const Key::Type ch) { m_input_queue.push(ch); } diff --git a/src/window.h b/src/window.h index 4860dd62..1b8862c1 100644 --- a/src/window.h +++ b/src/window.h @@ -49,82 +49,83 @@ namespace NC { namespace Key { -typedef const int Type; +typedef uint32_t Type; -Type None = -1; +const Type None = -1; // modifier masks -Type Alt = 1 << 16; -Type Ctrl = 1 << 17; -Type Shift = 1 << 18; +const Type Special = 1 << 31; +const Type Alt = 1 << 30; +const Type Ctrl = 1 << 29; +const Type Shift = 1 << 28; -Type Ctrl_A = 1; -Type Ctrl_B = 2; -Type Ctrl_C = 3; -Type Ctrl_D = 4; -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; +// useful names +const Type Null = 0; +const Type Space = 32; +const Type Backspace = 127; -Type Space = 32; -Type Backspace = 127; +// ctrl-? +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 -Type Tab = 9; -Type Enter = 13; -Type Escape = 27; +const Type Tab = 9; +const Type Enter = 13; +const Type Escape = 27; // special values, beyond one byte -Type Insert = 256; -Type Delete = 257; -Type Home = 258; -Type End = 259; -Type PageUp = 260; -Type PageDown = 261; - -Type Up = 262; -Type Down = 263; -Type Left = 264; -Type Right = 265; - -Type F1 = 266; -Type F2 = 267; -Type F3 = 268; -Type F4 = 269; -Type F5 = 270; -Type F6 = 271; -Type F7 = 272; -Type F8 = 273; -Type F9 = 274; -Type F10 = 275; -Type F11 = 276; -Type F12 = 277; - -Type Mouse = 278; +const Type Insert = Special | 256; +const Type Delete = Special | 257; +const Type Home = Special | 258; +const Type End = Special | 259; +const Type PageUp = Special | 260; +const Type PageDown = Special | 261; +const Type Up = Special | 262; +const Type Down = Special | 263; +const Type Left = Special | 264; +const Type Right = Special | 265; +const Type F1 = Special | 266; +const Type F2 = Special | 267; +const Type F3 = Special | 268; +const Type F4 = Special | 269; +const Type F5 = Special | 270; +const Type F6 = Special | 271; +const Type F7 = Special | 272; +const Type F8 = Special | 273; +const Type F9 = Special | 274; +const Type F10 = Special | 275; +const Type F11 = Special | 276; +const Type F12 = Special | 277; +const Type Mouse = Special | 278; } @@ -404,13 +405,13 @@ struct Window /// Reads key from standard input (or takes it from input queue) /// 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 - void pushChar(int ch); + void pushChar(const NC::Key::Type ch); /// @return const reference to internal input queue - const std::queue &inputQueue() { return m_input_queue; } + const std::queue &inputQueue() { return m_input_queue; } /// Scrolls the window by amount of lines given in its parameter /// @param where indicates how many lines it has to scroll @@ -479,7 +480,7 @@ protected: Border m_border; private: - int getInputChar(); + Key::Type getInputChar(int key); /// Sets state of bold attribute (internal use only) /// @param bold_state state of bold attribute @@ -515,7 +516,7 @@ private: /// input queue of a window. you can put characters there using /// PushChar and they will be immediately consumed and /// returned by ReadKey - std::queue m_input_queue; + std::queue m_input_queue; /// containter used for additional file descriptors that have /// to be polled in ReadKey() and correspondent callbacks that