diff --git a/experiments/shell/keyboard.hpp b/experiments/shell/keyboard.hpp index 546a1edc..cb9e2028 100644 --- a/experiments/shell/keyboard.hpp +++ b/experiments/shell/keyboard.hpp @@ -76,12 +76,30 @@ class KeyboardEncoder { return {true, modifier == 1 ? std::string("\x1b[") + final : std::string("\x1b[1;") + std::to_string(modifier) + final, false}; } + // F1..F4 use the SS3 letters; F5 and later use the xterm tilde numbers. + // The tilde numbers follow the VT/xterm table rather than the physical + // order so that applications see the same codes as other terminals. + static constexpr char ss3_function[] = {'P', 'Q', 'R', 'S'}; + if (key >= 0x70 && key <= 0x73) + if (key >= 0x70 && key <= 0x73) { + const char letter = ss3_function[key - 0x70]; + if (modifier == 1) return {true, std::string("\x1bO") + letter, false}; + return {true, std::string("\x1b[1;") + std::to_string(modifier) + letter, false}; + } int number = 0; switch (key) { case 0x2d: number = 2; break; // Insert case 0x2e: number = 3; break; // Delete case 0x21: number = 5; break; // PageUp case 0x22: number = 6; break; // PageDown + case 0x74: number = 15; break; // F5 + case 0x75: number = 17; break; // F6 + case 0x76: number = 18; break; // F7 + case 0x77: number = 19; break; // F8 + case 0x78: number = 20; break; // F9 + case 0x79: number = 21; break; // F10 + case 0x7a: number = 23; break; // F11 + case 0x7b: number = 24; break; // F12 default: return {}; } return {true, "\x1b[" + std::to_string(number) + diff --git a/experiments/shell/keyboard_test.cpp b/experiments/shell/keyboard_test.cpp index 317f6fe9..6a248cff 100644 --- a/experiments/shell/keyboard_test.cpp +++ b/experiments/shell/keyboard_test.cpp @@ -45,6 +45,18 @@ int main() { "application mode must not rewrite modified cursor keys"); check(KeyboardEncoder::key_down(0x2e, {}, true).bytes == "\x1b[3~", "application mode must not rewrite editing keys"); + // F1..F4 are SS3 letters; F5..F12 use the xterm tilde numbers. + const std::vector> functions{ + {0x70,"\x1bOP"}, {0x71,"\x1bOQ"}, {0x72,"\x1bOR"}, {0x73,"\x1bOS"}, + {0x74,"\x1b[15~"}, {0x75,"\x1b[17~"}, {0x76,"\x1b[18~"}, {0x77,"\x1b[19~"}, + {0x78,"\x1b[20~"}, {0x79,"\x1b[21~"}, {0x7a,"\x1b[23~"}, {0x7b,"\x1b[24~"}}; + for (const auto& entry : functions) { + const auto encoded = KeyboardEncoder::key_down(entry.first); + check(encoded.handled && encoded.bytes == entry.second && !encoded.skip_translate, + "function key encoding"); + } + check(KeyboardEncoder::key_down(0x70, {false,true,false,false}).bytes == "\x1b[1;5P", + "modified F1 must use the CSI modifier form"); check(KeyboardEncoder::key_down(0x24, {false,true,false,false}).bytes == "\x1b[1;5H", "Ctrl Home"); check(KeyboardEncoder::key_down(0x2e, {true,false,true,false}).bytes == "\x1b[3;4~", "Shift Alt Delete"); check(!KeyboardEncoder::key_down(0x09).handled, "plain Tab belongs only to WM_CHAR");