bindinds: add support for alt/ctrl/shift modifiers and escape key
This commit is contained in:
1
NEWS
1
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)
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
221
src/bindings.cpp
221
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<int>{ k.getChar() };
|
||||
if (k != Key::noOp)
|
||||
NC::Key::Type k = stringToSpecialKey(arg);
|
||||
auto queue = std::vector<NC::Key::Type>{ 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<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
|
||||
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<BindingIterator, BindingIterator> result;
|
||||
auto it = m_bindings.find(k);
|
||||
|
||||
@@ -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<wchar_t, Type> 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<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:
|
||||
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 <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));
|
||||
}
|
||||
|
||||
|
||||
68
src/help.cpp
68
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<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 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_)
|
||||
|
||||
@@ -29,14 +29,14 @@ namespace Actions {
|
||||
|
||||
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) { }
|
||||
|
||||
private:
|
||||
virtual void run() OVERRIDE;
|
||||
|
||||
NC::Window **m_window;
|
||||
std::vector<int> m_queue;
|
||||
std::vector<NC::Key::Type> m_queue;
|
||||
};
|
||||
|
||||
struct RequireRunnable: BaseAction
|
||||
|
||||
@@ -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
|
||||
|
||||
211
src/window.cpp
211
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,22 +970,39 @@ 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
|
||||
}
|
||||
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 != ';')
|
||||
@@ -916,6 +1014,7 @@ int Window::getInputChar()
|
||||
--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);
|
||||
}
|
||||
|
||||
145
src/window.h
145
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<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
|
||||
/// @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<int> m_input_queue;
|
||||
std::queue<Key::Type> m_input_queue;
|
||||
|
||||
/// containter used for additional file descriptors that have
|
||||
/// to be polled in ReadKey() and correspondent callbacks that
|
||||
|
||||
Reference in New Issue
Block a user