src: Fix mouse modifier support in the ncurses UI.

For historical reasons, mouse events represent keyboard modifiers as a bitfield,
but keyboard events represent modifiers as a bitfield-plus-one. For example, a
mouse event with an Alt modifier will use the value 4, but a keyboard event will
use the value 5.

Previously, I refactored the parse_mask() helper to do the subtraction itself,
instead of requiring the caller to do it. This made keyboard-event decoding much
cleaner, but I didn't realise it broke mouse-event decoding. Now the subtraction
is done only for keyboard events.

Fixes #4176.
This commit is contained in:
Tim Allen 2021-04-30 14:48:37 +10:00
parent 3b147bca0a
commit f78adeda49

View File

@ -668,7 +668,6 @@ Optional<Key> NCursesUI::get_next_key()
}; };
auto parse_mask = [](int mask) { auto parse_mask = [](int mask) {
mask = std::max(mask - 1, 0);
Key::Modifiers mod = Key::Modifiers::None; Key::Modifiers mod = Key::Modifiers::None;
if (mask & 1) if (mask & 1)
mod |= Key::Modifiers::Shift; mod |= Key::Modifiers::Shift;
@ -721,7 +720,10 @@ Optional<Key> NCursesUI::get_next_key()
(Codepoint)((down ? 1 : -1) * m_wheel_scroll_amount)}; (Codepoint)((down ? 1 : -1) * m_wheel_scroll_amount)};
}; };
auto masked_key = [&](Codepoint key) { return Key{parse_mask(params[1]), key}; }; auto masked_key = [&](Codepoint key) {
int mask = std::max(params[1] - 1, 0);
return Key{parse_mask(mask), key};
};
switch (c) switch (c)
{ {
@ -810,39 +812,40 @@ Optional<Key> NCursesUI::get_next_key()
raw_mask = raw_mask * 10 + (code - '0'); raw_mask = raw_mask * 10 + (code - '0');
code = get_char().value_or((unsigned char)0xff); code = get_char().value_or((unsigned char)0xff);
} while (code >= '0' and code <= '9'); } while (code >= '0' and code <= '9');
Key::Modifiers mod = parse_mask(std::max(raw_mask - 1, 0));
switch (code) switch (code)
{ {
case ' ': return Key{parse_mask(raw_mask), ' '}; case ' ': return Key{mod, ' '};
case 'A': return Key{parse_mask(raw_mask), Key::Up}; case 'A': return Key{mod, Key::Up};
case 'B': return Key{parse_mask(raw_mask), Key::Down}; case 'B': return Key{mod, Key::Down};
case 'C': return Key{parse_mask(raw_mask), Key::Right}; case 'C': return Key{mod, Key::Right};
case 'D': return Key{parse_mask(raw_mask), Key::Left}; case 'D': return Key{mod, Key::Left};
case 'F': return Key{parse_mask(raw_mask), Key::End}; case 'F': return Key{mod, Key::End};
case 'H': return Key{parse_mask(raw_mask), Key::Home}; case 'H': return Key{mod, Key::Home};
case 'I': return Key{parse_mask(raw_mask), Key::Tab}; case 'I': return Key{mod, Key::Tab};
case 'M': return Key{parse_mask(raw_mask), Key::Return}; case 'M': return Key{mod, Key::Return};
case 'P': return Key{parse_mask(raw_mask), Key::F1}; case 'P': return Key{mod, Key::F1};
case 'Q': return Key{parse_mask(raw_mask), Key::F2}; case 'Q': return Key{mod, Key::F2};
case 'R': return Key{parse_mask(raw_mask), Key::F3}; case 'R': return Key{mod, Key::F3};
case 'S': return Key{parse_mask(raw_mask), Key::F4}; case 'S': return Key{mod, Key::F4};
case 'X': return Key{parse_mask(raw_mask), '='}; case 'X': return Key{mod, '='};
case 'j': return Key{parse_mask(raw_mask), '*'}; case 'j': return Key{mod, '*'};
case 'k': return Key{parse_mask(raw_mask), '+'}; case 'k': return Key{mod, '+'};
case 'l': return Key{parse_mask(raw_mask), ','}; case 'l': return Key{mod, ','};
case 'm': return Key{parse_mask(raw_mask), '-'}; case 'm': return Key{mod, '-'};
case 'n': return Key{parse_mask(raw_mask), '.'}; case 'n': return Key{mod, '.'};
case 'o': return Key{parse_mask(raw_mask), '/'}; case 'o': return Key{mod, '/'};
case 'p': return Key{parse_mask(raw_mask), '0'}; case 'p': return Key{mod, '0'};
case 'q': return Key{parse_mask(raw_mask), '1'}; case 'q': return Key{mod, '1'};
case 'r': return Key{parse_mask(raw_mask), '2'}; case 'r': return Key{mod, '2'};
case 's': return Key{parse_mask(raw_mask), '3'}; case 's': return Key{mod, '3'};
case 't': return Key{parse_mask(raw_mask), '4'}; case 't': return Key{mod, '4'};
case 'u': return Key{parse_mask(raw_mask), '5'}; case 'u': return Key{mod, '5'};
case 'v': return Key{parse_mask(raw_mask), '6'}; case 'v': return Key{mod, '6'};
case 'w': return Key{parse_mask(raw_mask), '7'}; case 'w': return Key{mod, '7'};
case 'x': return Key{parse_mask(raw_mask), '8'}; case 'x': return Key{mod, '8'};
case 'y': return Key{parse_mask(raw_mask), '9'}; case 'y': return Key{mod, '9'};
default: return {}; default: return {};
} }
}; };