From aeaf39b8ea705e701e0bc8235214a093fff12cfe Mon Sep 17 00:00:00 2001
From: Arthur Sonzogni <sonzogniarthur@gmail.com>
Date: Tue, 18 Oct 2022 22:58:22 +0200
Subject: [PATCH] Fix F1-F4 keymapping. (#501)

It was just wrong, even on Linux.

Bug:https://github.com/ArthurSonzogni/FTXUI/issues/492
---
 CHANGELOG.md                                  |  3 +-
 examples/component/print_key_press.cpp        | 64 ++++++++++++++++++-
 src/ftxui/component/event.cpp                 | 26 ++++----
 src/ftxui/component/input.cpp                 |  2 +-
 src/ftxui/component/terminal_input_parser.cpp | 29 +++++----
 .../component/terminal_input_parser_test.cpp  | 10 +--
 6 files changed, 104 insertions(+), 30 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 29a6ae95..b594d5b4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -27,7 +27,8 @@ current (development)
 - Feature: `Input` supports CTRL+Left and CTRL+Right
 - Improvement: The `Menu` keeps the focus when an entry is selected with the
   mouse.
-- Bug: Add implementation of `ButtonOption::Border()`. It was missing.
+- Bugfix: Add implementation of `ButtonOption::Border()`. It was missing.
+- Bugfix: Provide the correct key for F1-F4 and F11.
 
 ### Screen
 - Feature: add `Box::Union(a,b) -> Box`
diff --git a/examples/component/print_key_press.cpp b/examples/component/print_key_press.cpp
index 8bcb1198..a42f2a43 100644
--- a/examples/component/print_key_press.cpp
+++ b/examples/component/print_key_press.cpp
@@ -25,7 +25,7 @@ std::string Stringify(Event event) {
 
   out = "(" + out + " ) -> ";
   if (event.is_character()) {
-    out += "character(" + event.character() + ")";
+    out += "Event::Character(\"" + event.character() + "\")";
   } else if (event.is_mouse()) {
     out += "mouse";
     switch (event.mouse().button) {
@@ -66,6 +66,68 @@ std::string Stringify(Event event) {
     out += "(" +  //
            std::to_string(event.mouse().x) + "," +
            std::to_string(event.mouse().y) + ")";
+  } else if (event == Event::ArrowLeft) {
+    out += "Event::ArrowLeft";
+  } else if (event == Event::ArrowRight) {
+    out += "Event::ArrowRight";
+  } else if (event == Event::ArrowUp) {
+    out += "Event::ArrowUp";
+  } else if (event == Event::ArrowDown) {
+    out += "Event::ArrowDown";
+  } else if (event == Event::ArrowLeftCtrl) {
+    out += "Event::ArrowLeftCtrl";
+  } else if (event == Event ::ArrowRightCtrl) {
+    out += "Event::ArrowRightCtrl";
+  } else if (event == Event::ArrowUpCtrl) {
+    out += "Event::ArrowUpCtrl";
+  } else if (event == Event::ArrowDownCtrl) {
+    out += "Event::ArrowDownCtrl";
+  } else if (event == Event::Backspace) {
+    out += "Event::Backspace";
+  } else if (event == Event::Delete) {
+    out += "Event::Delete";
+  } else if (event == Event::Escape) {
+    out += "Event::Escape";
+  } else if (event == Event::Return) {
+    out += "Event::Return";
+  } else if (event == Event::Tab) {
+    out += "Event::Tab";
+  } else if (event == Event::TabReverse) {
+    out += "Event::TabReverse";
+  } else if (event == Event::F1) {
+    out += "Event::F1";
+  } else if (event == Event::F2) {
+    out += "Event::F2";
+  } else if (event == Event::F3) {
+    out += "Event::F3";
+  } else if (event == Event::F4) {
+    out += "Event::F4";
+  } else if (event == Event::F5) {
+    out += "Event::F5";
+  } else if (event == Event::F6) {
+    out += "Event::F6";
+  } else if (event == Event::F7) {
+    out += "Event::F7";
+  } else if (event == Event::F8) {
+    out += "Event::F8";
+  } else if (event == Event::F9) {
+    out += "Event::F9";
+  } else if (event == Event::F10) {
+    out += "Event::F10";
+  } else if (event == Event::F11) {
+    out += "Event::F11";
+  } else if (event == Event::F12) {
+    out += "Event::F12";
+  } else if (event == Event::Home) {
+    out += "Event::Home";
+  } else if (event == Event::End) {
+    out += "Event::End";
+  } else if (event == Event::PageUp) {
+    out += "Event::PageUp";
+  } else if (event == Event::PageDown) {
+    out += "Event::PageDown";
+  } else if (event == Event::Custom) {
+    out += "Custom";
   } else {
     out += "(special)";
   }
diff --git a/src/ftxui/component/event.cpp b/src/ftxui/component/event.cpp
index 57d746af..1cd95ed1 100644
--- a/src/ftxui/component/event.cpp
+++ b/src/ftxui/component/event.cpp
@@ -65,18 +65,22 @@ const Event Event::Escape = Event::Special("\x1B");               // NOLINT
 const Event Event::Return = Event::Special({10});                 // NOLINT
 const Event Event::Tab = Event::Special({9});                     // NOLINT
 const Event Event::TabReverse = Event::Special({27, 91, 90});     // NOLINT
-const Event Event::F1 = Event::Special("\x1B[OP");                // NOLINT
-const Event Event::F2 = Event::Special("\x1B[OQ");                // NOLINT
-const Event Event::F3 = Event::Special("\x1B[OR");                // NOLINT
-const Event Event::F4 = Event::Special("\x1B[OS");                // NOLINT
-const Event Event::F5 = Event::Special("\x1B[15~");               // NOLINT
-const Event Event::F6 = Event::Special("\x1B[17~");               // NOLINT
-const Event Event::F7 = Event::Special("\x1B[18~");               // NOLINT
-const Event Event::F8 = Event::Special("\x1B[19~");               // NOLINT
-const Event Event::F9 = Event::Special("\x1B[20~");               // NOLINT
-const Event Event::F10 = Event::Special("\x1B[21~");              // NOLINT
-const Event Event::F11 = Event::Special("\x1B[21~");  // Doesn't exist // NOLINT
+
+// See https://invisible-island.net/xterm/xterm-function-keys.html
+// We follow xterm-new / vterm-xf86-v4 / mgt / screen
+const Event Event::F1 = Event::Special("\x1BOP");     // NOLINT
+const Event Event::F2 = Event::Special("\x1BOQ");     // NOLINT
+const Event Event::F3 = Event::Special("\x1BOR");     // NOLINT
+const Event Event::F4 = Event::Special("\x1BOS");     // NOLINT
+const Event Event::F5 = Event::Special("\x1B[15~");   // NOLINT
+const Event Event::F6 = Event::Special("\x1B[17~");   // NOLINT
+const Event Event::F7 = Event::Special("\x1B[18~");   // NOLINT
+const Event Event::F8 = Event::Special("\x1B[19~");   // NOLINT
+const Event Event::F9 = Event::Special("\x1B[20~");   // NOLINT
+const Event Event::F10 = Event::Special("\x1B[21~");  // NOLINT
+const Event Event::F11 = Event::Special("\x1B[23~");  // NOLINT
 const Event Event::F12 = Event::Special("\x1B[24~");  // NOLINT
+
 const Event Event::Home = Event::Special({27, 91, 72});           // NOLINT
 const Event Event::End = Event::Special({27, 91, 70});            // NOLINT
 const Event Event::PageUp = Event::Special({27, 91, 53, 126});    // NOLINT
diff --git a/src/ftxui/component/input.cpp b/src/ftxui/component/input.cpp
index b2e4ad90..d56ce4be 100644
--- a/src/ftxui/component/input.cpp
+++ b/src/ftxui/component/input.cpp
@@ -48,7 +48,7 @@ bool IsWordCharacter(WordBreakProperty property) {
     case WordBreakProperty::Regional_Indicator:
     case WordBreakProperty::ZWJ:
       return false;
-  };
+  }
   return true; // NOT_REACHED();
 }
 
diff --git a/src/ftxui/component/terminal_input_parser.cpp b/src/ftxui/component/terminal_input_parser.cpp
index 0f0a036f..d3f2d6a4 100644
--- a/src/ftxui/component/terminal_input_parser.cpp
+++ b/src/ftxui/component/terminal_input_parser.cpp
@@ -3,14 +3,24 @@
 #include <cstdint>                    // for uint32_t
 #include <ftxui/component/mouse.hpp>  // for Mouse, Mouse::Button, Mouse::Motion
 #include <ftxui/component/receiver.hpp>  // for SenderImpl, Sender
-#include <memory>                        // for unique_ptr, allocator
-#include <utility>                       // for move
+#include <map>
+#include <memory>   // for unique_ptr, allocator
+#include <utility>  // for move
 
 #include "ftxui/component/event.hpp"  // for Event
 #include "ftxui/component/task.hpp"   // for Task
 
 namespace ftxui {
 
+// NOLINTNEXTLINE
+const std::map<std::string, std::string> g_uniformize = {{
+    // Microsoft's terminal uses a different new line character for the return
+    // key. This also happens with linux with the `bind` command:
+    // See https://github.com/ArthurSonzogni/FTXUI/issues/337
+    // Here, we uniformize the new line character to `\n`.
+    {"\r", "\n"},
+}};
+
 TerminalInputParser::TerminalInputParser(Sender<Task> out)
     : out_(std::move(out)) {}
 
@@ -56,17 +66,14 @@ void TerminalInputParser::Send(TerminalInputParser::Output output) {
       pending_.clear();
       return;
 
-    case SPECIAL:
-      // Microsoft's terminal uses a different new line character for the return
-      // key. This also happens with linux with the `bind` command:
-      // See https://github.com/ArthurSonzogni/FTXUI/issues/337
-      // Here, we uniformize the new line character to `\n`.
-      if (pending_ == "\r") {
-        out_->Send(Event::Special("\n"));
-      } else {
-        out_->Send(Event::Special(std::move(pending_)));
+    case SPECIAL: {
+      auto it = g_uniformize.find(pending_);
+      if (it != g_uniformize.end()) {
+        pending_ = it->second;
       }
+      out_->Send(Event::Special(std::move(pending_)));
       pending_.clear();
+    }
       return;
 
     case MOUSE:
diff --git a/src/ftxui/component/terminal_input_parser_test.cpp b/src/ftxui/component/terminal_input_parser_test.cpp
index 24f611d4..922ee198 100644
--- a/src/ftxui/component/terminal_input_parser_test.cpp
+++ b/src/ftxui/component/terminal_input_parser_test.cpp
@@ -347,17 +347,17 @@ TEST(Event, Special) {
       {{10}, Event::Return},
       {{9}, Event::Tab},
       {{27, 91, 90}, Event::TabReverse},
-      //{str("\x1B[OP"), Event::F1},
-      //{str("\x1B[OQ"), Event::F2},
-      //{str("\x1B[OR"), Event::F3},
-      //{str("\x1B[OS"), Event::F4},
+      {str("\x1BOP"), Event::F1},
+      {str("\x1BOQ"), Event::F2},
+      {str("\x1BOR"), Event::F3},
+      {str("\x1BOS"), Event::F4},
       {str("\x1B[15~"), Event::F5},
       {str("\x1B[17~"), Event::F6},
       {str("\x1B[18~"), Event::F7},
       {str("\x1B[19~"), Event::F8},
       {str("\x1B[20~"), Event::F9},
       {str("\x1B[21~"), Event::F10},
-      {str("\x1B[21~"), Event::F11},
+      {str("\x1B[23~"), Event::F11},
       {str("\x1B[24~"), Event::F12},
       {{27, 91, 72}, Event::Home},
       {{27, 91, 70}, Event::End},
-- 
GitLab