gyscos / cursive

A Text User Interface library for the Rust programming language
MIT License
4.25k stars 244 forks source link

Handle a "modifier+bare key" key combination #151

Open hcpl opened 7 years ago

hcpl commented 7 years ago

Description

Cannot process a "modifier+bare key" key combination, only a "bare key".

Configuration

$ uname -srm
Linux 4.2.0-42-generic x86_64
$ rustc --version
rustc 1.18.0 (03fc9d622 2017-06-06)
$ gnome-terminal --version
GNOME Terminal 3.6.2

Cursive version 0.6.4, the issue is relevant to all backends except BearLibTerminal which I didn't test.

Expected behaviour

Print on the upper-left corner:

Actual behaviour

Nothing happens.

Code to reproduce

extern crate cursive;

use cursive::{Cursive, Printer};
use cursive::event::{Event, EventResult, Key};
use cursive::vec::Vec2;
use cursive::view::View;

struct Foo {
    info: &'static str,
}

impl View for Foo {
    fn draw(&self, printer: &Printer) {
        printer.print((0, 0), self.info);
    }

    fn required_size(&mut self, constraint: Vec2) -> Vec2 {
        Vec2::from((self.info.len(), 1)).or_min(constraint)
    }

    fn on_event(&mut self, event: Event) -> EventResult {
        self.info = match event {
            //Event::Key(Key::Enter) => "enter",
            Event::Ctrl(Key::Enter) => "ctrl+enter",
            Event::Shift(Key::Enter) => "shift+enter",
            Event::Alt(Key::Enter) => "alt+enter",
            _ => return EventResult::Ignored,
        };

        EventResult::Consumed(None)
    }
}

fn main() {
    let mut siv = Cursive::new();
    siv.add_fullscreen_layer(Foo { info: "" });
    siv.run();
}

P.S. If the Event::Key(Key::Enter) => "enter" part is uncommented, the program will print enter for all 4 cases.

UPD: Add gnome-terminal as the terminal used and its version.

gyscos commented 7 years ago

Hi, and thanks for the report!

The "Enter" key is a special one, and isn't very well supported with modifier keys - terminals just don't send the complete information to the application. Ctrl-Enter and Shift-Enter are just sent as simple Enter event; though Alt+Enter should be detectable with some modification to the input system. If you try with another key, like Home, it should work better.

Now, there seems to be some problem detecting this modifier. Apparently Ctrl-Home is detected as Ctrl-Shift-Home, and some other confusion, so I'll need to have a look at that part. :S

As a note, you can use the key_codes example to see the event generated by a key press.

hcpl commented 7 years ago

Oh, I missed the example, thanks for pointing out.

Since ncurses does not support new features of modern terminals I already knew that keyboard handling is messy, but I never thought it's that messy. So I came up with a table:

No modifiers Shift Ctrl Alt Ctrl+Shift Ctrl+Alt Alt+Shift Ctrl+Alt+Shift
Enter Key(Enter) Key(Enter) Key(Enter) Key(Esc) Key(Enter) Key(Enter) Key(Esc) Key(Enter) Key(Esc) Key(Enter) Key(Esc) Key(Enter)
Home Key(Home) Nothing Key(Home) Key(Home) Nothing Key(Home) Nothing Nothing
End Key(End) Nothing Key(End) Key(End) Nothing Key(End) Nothing Nothing
PageUp Key(PageUp) Nothing CtrlAlt(PageDown) Ctrl(PageDown) Nothing AltShift(PageUp) Nothing Nothing
PageDown Key(PageDown) Nothing CtrlAlt(Left) Ctrl(Left) Nothing AltShift(PageDown) Nothing Nothing
Left Key(Left) Shift(Left) CtrlAlt(Ins) Ctrl(Ins) Alt(Left) AltShift(Left) Unknown([31, 2, 0, 0]) Key(Esc) Char('[') Char('1') Char(';') Char('8') Char('D')
Right Key(Right) Shift(Right) CtrlAlt(PageUp) Ctrl(PageUp) Alt(Right) AltShift(Right) CtrlShift(PageUp) Key(Esc) Char('[') Char('1') Char(';') Char('8') Char('C')
Up Key(Up) Shift(Up) Unknown([53, 2, 0, 0]) CtrlShift(Right) Nothing AltShift(Up) CtrlAlt(Right) Nothing
Down Key(Down) Shift(Down) Unknown([12, 2, 0, 0]) CtrlShift(Del) Nothing AltShift(Down) Unknown([11, 2, 0, 0]) Nothing

This shows that there are many cases where one modifier set is recognized as another one, some send an escape sequence, some are not recognized by Cursive and others are trapped entirely, though I don't know what exactly in keyboard OS event chain should be blamed for trapping.

Also your observation

Apparently Ctrl-Home is detected as Ctrl-Shift-Home

contradicts with mine: it's detected as plain Home. I think that's because of difference in keyboard layouts, either (or even both) in hardware and in software (I am using i3wm with modifier key bound on Alt but to fill this table I temporarily switched it off).

gyscos commented 7 years ago

Another note: the terminal used has a large impact on the events actually delivered to the application.

I just pushed a1737ca0ba1a99bffc5ec6f42e3371c9df552a91, which at least fixes most modifier+key input on my computer for the ncurses backend. I tested it locally with gnome-terminal and konsole. Not all key combinations work on both, but I didn't see any Unknown or mislabeled event (except for Ctrl+Shift+Alt+Key, those are a bit trickier for now). If this work for you, I'll update the pancurses backend to work the same.

If this doesn't work for you, then it means the key codes are different between multiple systems (could be terminfo version, terminal used, ...), which would be a bit of a pain...

Notes:

For these reasons, I used to have in the Readme a compatibility table of the events available on various terminals, but it ended up being out-of-date, it depended on the backend used, and was overall too much work to maintain.

hcpl commented 7 years ago

Yeah, I only tested on gnome-terminal. I also have xterm and urxvt installed, so I'll test your commit against them too.

gyscos commented 6 years ago

After realizing that the actual key <=> code mapping was changing between ncurses versions, I understood hard-coding this table wasn't going anywhere. So now it should detect this mapping at runtime. This include the (Alt, AltShift, Ctrl, CtrlShift and CtrlAlt) modifiers, for the (Del, Insert, Home, End,PageUp/Down` and the arrow keys).

mx-shift commented 6 years ago

I'm seeing incorrect mapping with macOS Terminal and the default backend. For example, Alt(Left) appears to be an escape code of Key(Esc) Char('b'). When I get some time, I'll dig into the code to see what can be done.

ghost commented 6 years ago

What's the future of this task? Is it doable?

gyscos commented 6 years ago

TLDR: Shift+Enter and Ctrl+Enter are probably impossible to differentiate from Enter, because the terminal sends the same code to the application.

Input encoding depends on the terminal used, and those can vary a lot. Recognizing the encoding scheme and parsing the input appropriately is a huge task, and ncurses is supposed to do exactly that. As much as possible, I'd like to leave most of the $TERM-dependent work to the backend library (or, if needed, the backend module).

That being said, parsing the output from ncurses itself isn't always trivial; many key codes are correctly parsed, but the returned code isn't documented, and the actual value depends on the ncurses version. This is what can be improved here - for instance, confusing CtrlAlt(Ins) and Ctrl(Left) results from wrongly parsing ncurses' own output, and should be correctable. Receiving Key(Esc) and Char('b') instead of Alt(Left) means ncurses is not parsing the escape sequence for this particular terminal, and is trickier to fix here.

Regarding macOS compatibility, I don't have a machine to test; https://github.com/gyscos/Cursive/issues/206 may be of interest.

Regarding variations for the Enter key, most terminals simply send the same code even if Shift or Ctrl is pressed, so cursive cannot tell the difference.

As for the termion backend, it doesn't currently check the current terminal, but there's an issue for that (https://github.com/ticki/termion/issues/106).

massimo79m commented 1 year ago

Same behaviour on macos terminal. i need the ctrl-enter but it does not work.