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.
* 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)

View File

@@ -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.

View File

@@ -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;

View File

@@ -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);

View File

@@ -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));
}

View File

@@ -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_)

View File

@@ -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

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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