ArthurSonzogni / FTXUI

:computer: C++ Functional Terminal User Interface. :heart:
MIT License
6.73k stars 401 forks source link

Mouse input does not work correctly on urxvt: Moving the cursor always claims the mouse button is clicked. #791

Closed psychon closed 9 months ago

psychon commented 9 months ago

Hi,

running the print_key_press example in urxvt and moving the mouse around produces e.g.

( 27 91 60 51 50 59 56 53 59 52 50 77 ) -> mouse_left_pressed_control(84,9)

Notice that this incorrectly claims that the left mouse button is pressed.

The same thing in xterm:

( 27 91 60 51 53 59 55 57 59 49 53 77 ) -> mouse_none_pressed_control(78,13)

This issue makes basically anything interactive unusable. E.g. button will trigger every time the cursor moves slightly across it.

ArthurSonzogni commented 9 months ago

Hello @psychon

Thanks for reporting this

I think this was improved ~30 days ago with: https://github.com/ArthurSonzogni/FTXUI/pull/774

Every builtin components have been updated. A new API for custom on is available

  bool Mouse::IsPressed(Button button) const;
  bool Mouse::IsHeld(Button button) const;
  bool Mouse::IsReleased(Button button) const;

Does the new version satisfies you, or do we need something different?

That being said, the example print_key_press would still report the same kind of result.

psychon commented 9 months ago

Ah, you are right. I was initially experimenting with the latest release and then switched to latest main before reporting this issue.

However, "things" still don't seem to work correctly. I switched to the component/dropdown example. My instructions:

  1. click an entry (to open the dropdown)
  2. move the mouse to an item
  3. click
  4. move the mouse

With xterm, the dropdown closes at step 3 and the mouse move has no effect. With urxvt, the mouse click has no effect and only at step 4 does "something" happen.

Put differently: Yes, I can now interact with the dropdown via the mouse, but things still behave weird / unexpected.

I checked out git at c31aecf2edde81d20f4f4bc8c97af053026483a6^ (= before #774) and there things indeed behave as I described out. That's a lot worse than the current state of things.

psychon commented 9 months ago

I found http://pod.tst.eu/http://cvs.schmorp.de/rxvt-unicode/doc/rxvt.7.pod#Mouse_Reporting and the following sub-sections. ftxui seems to enable 1000, 1003, 1015, and 1016.

I did some more tests showing the raw input that was received in a human readable form:

diff --git a/examples/component/print_key_press.cpp b/examples/component/print_key_press.cpp
index c481271..221f93a 100644
--- a/examples/component/print_key_press.cpp
+++ b/examples/component/print_key_press.cpp
@@ -22,6 +22,9 @@ std::string Stringify(Event event) {
   std::string out;
   for (auto& it : event.input())
     out += " " + std::to_string((unsigned int)it);
+  out += " ";
+  for (auto& it : event.input())
+    out += it;

   out = "(" + out + " ) -> ";
   if (event.is_character()) {

I am just showing the printable part of the above output.

So... the moves are the last button that was pressed with 32 added?

With the following proof-of-concept patch, the print_key_press example seems to produce sane output.

diff --git a/src/ftxui/component/terminal_input_parser.cpp b/src/ftxui/component/terminal_input_parser.cpp
index 3ba0e69..ec3cc3c 100644
--- a/src/ftxui/component/terminal_input_parser.cpp
+++ b/src/ftxui/component/terminal_input_parser.cpp
@@ -401,6 +401,10 @@ TerminalInputParser::Output TerminalInputParser::ParseMouse(  // NOLINT
   output.mouse.meta = bool(arguments[0] & 8);                       // NOLINT
   output.mouse.x = arguments[1];                                    // NOLINT
   output.mouse.y = arguments[2];                                    // NOLINT
+  if (arguments[0] & 32) {
+    // DECMode::kMouseSgrExtMode: This is a move and not a press
+    output.mouse.button = Mouse::Button::None;
+  }
   return output;
 }

How did I come up with the above? The following part of the urxvt docs (but ignoring the offset since mode 1015 does not include one):

Bit 5 of b is set for motion events or double clicks (rxvt extension, disabled by default):

Motion = (b - SPACE) & 32

However, if I open a new terminal, newer press a button and move the mouse, I get e.g. [<31;78;45M. Button -1 pressed?!?

I also dug into urxvt's source code. The mouse reporting "stuff" is generated here: http://cvs.schmorp.de/rxvt-unicode/src/command.C?revision=1.603&view=markup#l1353

So... since ftxui enables mode 1006 (kMouseSgrExtMode / PrivMode_ExtMouseSGR), all the docs for urxvt mode do not apply. And I could have actually noticed that the escape sequences above have [< and the < would not be present in urxvt mode... Besides that, I have not found anything helpful in this source code. I am now trying to figure out how this code remembers the last button that was pressed?!?

Edit: Found it. MEvent is a member variable of the class and is used to track this. The button_number is calculated at MEvent.button - Button1. At startup, MEvent.button is initialised to AnyButton and AnyButton - Button1 is -1. I'll report a bug to urxvt.

@ArthurSonzogni I hope some of this helps you help me. I never dug into escape sequences before.

ArthurSonzogni commented 9 months ago

Thanks you very much!

You made me realize 3 things:

I start working on something. I will send you a PR.