Skip to content

Commit

Permalink
Prevent input sequences without key down
Browse files Browse the repository at this point in the history
  • Loading branch information
houmain committed Dec 29, 2023
1 parent 6b08ff2 commit afdc83f
Show file tree
Hide file tree
Showing 6 changed files with 28 additions and 12 deletions.
7 changes: 4 additions & 3 deletions src/config/ParseConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ void ParseConfig::parse_context(It* it, const It end) {
else if (attrib == "modifier") {
auto modifier = read_value(it, end);
m_context_modifier = m_parse_sequence(
preprocess(modifier.begin(), modifier.end()), true,
preprocess(modifier.begin(), modifier.end()), true, true,
std::bind(&ParseConfig::get_key_by_name, this, _1));
m_context_modifier.erase(
std::remove_if(m_context_modifier.begin(), m_context_modifier.end(),
Expand Down Expand Up @@ -296,8 +296,9 @@ void ParseConfig::parse_command_and_mapping(const It in_begin, const It in_end,

KeySequence ParseConfig::parse_input(It it, It end) try {
skip_space(&it, end);
auto sequence = m_parse_sequence(preprocess(it, end), true,
auto sequence = m_parse_sequence(preprocess(it, end), true, false,
std::bind(&ParseConfig::get_key_by_name, this, _1));

sequence.insert(sequence.begin(),
m_context_modifier.begin(), m_context_modifier.end());
return sequence;
Expand Down Expand Up @@ -327,7 +328,7 @@ Key ParseConfig::add_terminal_command_action(std::string_view command) {

KeySequence ParseConfig::parse_output(It it, It end) try {
skip_space(&it, end);
return m_parse_sequence(preprocess(it, end), false,
return m_parse_sequence(preprocess(it, end), false, true,
std::bind(&ParseConfig::get_key_by_name, this, _1),
std::bind(&ParseConfig::add_terminal_command_action, this, _1));
}
Expand Down
15 changes: 13 additions & 2 deletions src/config/ParseKeySequence.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,22 @@ namespace {
default: return key;
}
}

bool has_key_down(const KeySequence& sequence) {
return std::count_if(sequence.rbegin(), sequence.rend(),
[&](const KeyEvent& event) {
return (event.state == KeyState::Down && event.key != Key::timeout);
});
}
} // namespace

KeySequence ParseKeySequence::operator()(
std::string_view str, bool is_input,
std::string_view str, bool is_input, bool allow_empty,
GetKeyByName get_key_by_name,
AddTerminalCommand add_terminal_command) {

m_is_input = is_input;
m_allow_empty = allow_empty;
m_get_key_by_name = std::move(get_key_by_name);
m_add_terminal_command = std::move(add_terminal_command);
m_keys_not_up.clear();
Expand Down Expand Up @@ -135,7 +143,7 @@ Key ParseKeySequence::read_key(It* it, const It end) {

void ParseKeySequence::add_timeout_event(KeyState state, uint16_t timeout) {
flush_key_buffer(true);
if (m_is_input && m_sequence.empty())
if (m_is_input && !has_key_down(m_sequence))
throw ParseError("Input sequence must not start with timeout");
if (!m_is_input && state == KeyState::Not)
throw ParseError("Ouput sequence must not contain a not-timeout");
Expand Down Expand Up @@ -344,4 +352,7 @@ void ParseKeySequence::parse(It it, const It end) {
if (!has_modifier && all_pressed_at_once())
remove_any_up_from_end();
}

if (!m_allow_empty && !has_key_down(m_sequence))
throw ParseError("sequence contains no key down");
}
3 changes: 2 additions & 1 deletion src/config/ParseKeySequence.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class ParseKeySequence {
using GetKeyByName = std::function<Key(std::string_view)>;
using AddTerminalCommand = std::function<Key(std::string_view)>;

KeySequence operator()(std::string_view str, bool is_input,
KeySequence operator()(std::string_view str, bool is_input, bool allow_empty,
GetKeyByName get_key_by_name = ::get_key_by_name,
AddTerminalCommand add_terminal_command = { });

Expand All @@ -68,6 +68,7 @@ class ParseKeySequence {
void remove_any_up_from_end();

bool m_is_input{ };
bool m_allow_empty{ };
GetKeyByName m_get_key_by_name;
AddTerminalCommand m_add_terminal_command;
std::optional<StringTyper> m_string_typer;
Expand Down
4 changes: 2 additions & 2 deletions src/test/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,12 @@ std::ostream& operator<<(std::ostream& os, const KeyEvent& event) {

KeySequence parse_input(const char* input) {
static auto parse = ParseKeySequence();
return parse(input, true);
return parse(input, true, false);
}

KeySequence parse_output(const char* output) {
static auto parse = ParseKeySequence();
return parse(output, false);
return parse(output, false, true);
}

KeySequence parse_sequence(const char* it, const char* const end) {
Expand Down
7 changes: 3 additions & 4 deletions src/test/test0_ParseKeySequence.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

TEST_CASE("Input Expression", "[ParseKeySequence]") {
// Empty
CHECK(parse_input("") == (KeySequence{ }));
CHECK_THROWS(parse_input(""));

// A has to be pressed.
// "A" => +A ~A
Expand Down Expand Up @@ -102,9 +102,6 @@ TEST_CASE("Input Expression", "[ParseKeySequence]") {
}));

// Not
CHECK(parse_input("!A") == (KeySequence{
KeyEvent(Key::A, KeyState::Not),
}));
CHECK(parse_input("A !A B") == (KeySequence{
KeyEvent(Key::A, KeyState::Down),
KeyEvent(Key::A, KeyState::UpAsync),
Expand All @@ -113,9 +110,11 @@ TEST_CASE("Input Expression", "[ParseKeySequence]") {
KeyEvent(Key::B, KeyState::UpAsync),
}));
CHECK_THROWS(parse_input("!"));
CHECK_THROWS(parse_input("!A"));
CHECK_THROWS(parse_input("!(A B)"));
CHECK_THROWS(parse_input("!A{B}"));
CHECK_THROWS(parse_input("A{!B}"));
CHECK_THROWS(parse_input("!A 100ms"));

// Output on release
CHECK_THROWS(parse_input("A ^ B"));
Expand Down
4 changes: 4 additions & 0 deletions src/test/test1_ParseConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ TEST_CASE("Valid config", "[ParseConfig]") {
//--------------------------------------------------------------------

TEST_CASE("Problems", "[ParseConfig]") {
// input without Down
CHECK_THROWS(parse_config(R"(!A >> B)"));
CHECK_THROWS(parse_config(R"(!A 100ms >> B)"));

// not mapped command
auto string = R"(
C >> CommandA
Expand Down

0 comments on commit afdc83f

Please sign in to comment.