Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch away from ncurses to terminfo-based input handling #2554

Closed
maximbaz opened this issue Nov 4, 2018 · 25 comments
Closed

Switch away from ncurses to terminfo-based input handling #2554

maximbaz opened this issue Nov 4, 2018 · 25 comments

Comments

@maximbaz
Copy link
Contributor

maximbaz commented Nov 4, 2018

This was brought up many times, and is indirectly implied by a few existing issues asking to distinguish certain keyboard mappings regardless of the taken approach. Contrary to all those issues, this ticket is proposing a specific approach: switching away from ncurses to terminfo-based input handling.

Input handling is a key aspect of code editor, I strongly believe this should be done directly using terminfo and not via some third-party wrapper libraries such as ncurses or libtickit. The current limitations of ncurses are simply embarrassing. Terminals evolve, if a new solution / xterm protocol extension gets adopted, we must be able to enable it quickly, not wait for months or years until it's adopted by a library of choice.

If this is implemented, this fixes issues like #2553, #2209, #1527, #1270, #997, #970 and #1012.

@Delapouite
Copy link
Contributor

I agree. Just to clarify, do you suggest we should also drop ncurses for rendering or should this be tackled in a separate task?

@maximbaz
Copy link
Contributor Author

maximbaz commented Nov 4, 2018

If possible, I suggest to tackle that separately, primarily to simplify the transition.

@Screwtapello
Copy link
Contributor

Abandoning ncurses' input-handling and using raw terminfo would make it easier to handle keys like <c-left> (which are listed in the terminfo database), but it wouldn't help with most of the issues you list:

@maximbaz
Copy link
Contributor Author

maximbaz commented Nov 4, 2018

Well somehow nvim is able to distinguish certain mappings that kakoune cannot (such as c-j and ret), so I'm sure it's possible. Furthermore, if we use terminfo-based input handling, it is very simple to support modern terminals - for example kitty supports something called full keyboard mode that allows to distinguish each and every key combination, and in order to support that the application needs to simply send out a single escape code that some terminals will respect and others will silently ignore. This is impossible with ncurses or libtickit.

I admit my collection of issues might not be perfect, I mostly wanted to showcase a scale of how often this topic is brought up.

@Screwtapello
Copy link
Contributor

Normally <ret> sends <c-m>, but because the line-ending convention on Unix is <c-j>, ncurses by default converts <c-m> to <c-j> so they're indistinguishable. ncurses also provides the nonl() function to disable that conversion and keep them separate, which Kakoune uses. However, Kakoune re-implements the conversion itself, I'm not sure why. That still leaves <ret> and <c-m> as indistinguishable, but I believe that's the case in (N)Vim too.

Kitty's "full keyboard mode" does not appear in its terminfo database, so terminfo support won't help with that.

Just to be clear, I'm not arguing that we shouldn't replace ncurses input handling with raw terminfo handling. I'm sure it would make the code cleaner, and a bit more flexible since some extended terminfo keys (like <c-right>) are a pain to use via the ncurses API. But unlike output formatting (where terminfo is much more flexible than ncurses, and raw codes much more flexible again), terminfo input handling isn't that much of an advantage.

@maximbaz
Copy link
Contributor Author

maximbaz commented Nov 5, 2018

Thanks for explaining, interesting. In summary, if I understood everything correctly, by switching to raw handling we will be limited not by a library of choice, but by terminals themselves: for old terminals there will be little advantage, but for modern terminals we will be able to distinguish more, or even all keyboard shortcuts. As a user of a modern terminal I consider this a big advantage 😄

I'll open an issue in kitty so by the time this ticket is addressed, kitty's terminfo database will be ready for full keyboard mode 😉

@Delapouite
Copy link
Contributor

Delapouite commented Nov 5, 2018

Thanks for reaching out to kitty @maximbaz Plainly supporting modern terminals will also mean having nice support for UI features like curly underlines.

With the goal of fully removing ncurses in the long run (kakoune will be dependency-free), I think it would make sense to approach the development by copying ncurses_ui.hh and ncurses_ui.cc to new files term_ui.hh and term_ui.cc.
This way we could iterate on this new code without risk of touching the existing one, because I expect we may inadvertently break things in the beginning on a few edge cases. Beta users could then start kakoune with kak -ui term to see if it works for them.
It is a good opportunity to gradually include pieces, starting by the keyboard input and then the rendering part, and gather feedback along the way for a broad audience.

@maximbaz
Copy link
Contributor Author

maximbaz commented Nov 5, 2018

Just want to mention, this is another perfect example why removing dependency is beneficial: I reported an issue to kitty and 2 hours later it was fixed, new terminfo file is already available for download. Imagine how many years it would take for ncurses to update kitty's terminfo before ncurses will be able to detect that kitty supports this new term capability (I assume this is how it works, this is why ncurses bundles various terminfo files in its distribution).

@mawww
Copy link
Owner

mawww commented Nov 6, 2018

With the goal of fully removing ncurses in the long run (kakoune will be dependency-free), I think it would make sense to approach the development by copying ncurses_ui.hh and ncurses_ui.cc to new files term_ui.hh and term_ui.cc.

I think I would prefer to avoid duplicating all that code. If we want to experiment while keeping a fallback (not really the traditional Kakoune way, which has often been to just break it), adding a ncurses_input_stack=ncurses|terminfo ui_option would do the trick.

Once we get rid of ncurses, we just have to remove our dependency on the standard C++ library and we are done ! (kidding, or am I ?)

@andreyorst
Copy link
Contributor

Once we get rid of ncurses, we just have to remove our dependency on the standard C++ library and we are done ! (kidding, or am I ?)

And then rewrite everything in Rust.

@lenormf
Copy link
Contributor

lenormf commented Nov 6, 2018

I think I would prefer to avoid duplicating all that code. If we want to experiment while keeping a fallback (not really the traditional Kakoune way, which has often been to just break it), adding a ncurses_input_stack=ncurses|terminfo ui_option would do the trick.

Since there's no real point to keep ncurses for that job in the long run, why not just create a branch that removes ncurses? Once it's ready/tested then we can just merge the branch into master.

@Delapouite
Copy link
Contributor

Let's try to recap the current state of the art to see if we're on the same page.

As far as I understand, both libtermkey and libtickit libraries share the same author. libtermkey is now deprecated since it handles only input and libtickit is a superset which also does the drawing part.

http://www.leonerd.org.uk/code/libtermkey/
http://www.leonerd.org.uk/code/libtickit/

libtermkey is used by vis and neovim

The idea behind these libs is explained in http://www.leonerd.org.uk/hacks/fixterms/

But then, 1 year ago, the authors of libtermkey, neovim and kitty exchanged a few words on this topic: kovidgoyal/kitty#94 (comment)
They discussed the libtermkey solution vs the one implemented by kitty: https://sw.kovidgoyal.net/kitty/protocol-extensions.html#keyboard-handling

From what I understand, the kitty approach seem more complete than libtermkey.
The concept of lossless keyboard input was also a strong argument during the (now suspended) development of notty: https://github.com/withoutboats/notty

Don't hesitate to correct me or add other pieces of info, so we can get a broader image of the current solutions.

@andreyorst
Copy link
Contributor

One small question: if Kakoune will ever have official GUI (what? I think that the project can benefit a lot if it will have both TUI and GUI, like Emacs. It also adds lots of features beyond fancy window), would it be as restricted in terms of keyboard support as it's terminal version?

E.g. in Emacs some keys are bound to corresponding terminal ones by default, like Ctrl+m is mapped to Return, despite the fact that GUI Emacs can distinguish it from return key, but that brings consistent behaviour between GUI and TUI.

@Screwtapello
Copy link
Contributor

Since there is no official GUI yet, nobody can definitively say what it would or would not be able to do. However, I've worked on an unofficial GUI, and (among other things) it can definitely distinguish <c-m>, <c-j> and <ret>. On the other hand, Kakoune only allows the shift modifier to be used with ASCII letters and special keys, not other printable characters, and that restriction does remain in my unofficial GUI.

@mawww
Copy link
Owner

mawww commented Nov 6, 2018

Since there's no real point to keep ncurses for that job in the long run, why not just create a branch that removes ncurses? Once it's ready/tested then we can just merge the branch into master.

We can do the work progressively on master, fix whats detected as broken, and once ncurses_ui does not use ncurses anymore, rename it.

One small question: if Kakoune will ever have official GUI (what? I think that the project can benefit a lot if it will have both TUI and GUI, like Emacs. It also adds lots of features beyond fancy window), would it be as restricted in terms of keyboard support as it's terminal version?

Its unlikely that Kakoune will come bundled with an official GUI, as this would mean blessing some kind of UI toolkit and dependency. UIs can be implemented through the json-rpc ui protocol, and I designed the UI system with non-terminal UIs in mind (although its not very well tested, so as @Screwtapello said, their might be some arbitrary restrictions we need to lift). Except for a few hacks, Kakoune should not have any terminal specific knowledge outside of ncurses_ui.cc.

@maximbaz
Copy link
Contributor Author

maximbaz commented Nov 6, 2018

We can do the work progressively on master, fix whats detected as broken, and once ncurses_ui does not use ncurses anymore, rename it.

I support this approach as well.

jeapostrophe added a commit to jeapostrophe/kakoune that referenced this issue Nov 28, 2018
Ideally, something better should be done (re mawww#2554) but this is a decent
intermediate step for some useful keys.

Note: NCurses supports parsing these keys when shifted (KEY_SR,
_SLEFT, S_RIGHT, etc), but it does not do the same thing for the other
modifiers.
jeapostrophe added a commit to jeapostrophe/kakoune that referenced this issue Dec 7, 2018
Ideally, something better should be done (re mawww#2554) but this is a decent
intermediate step for some useful keys.

Note: NCurses supports parsing these keys when shifted (KEY_SR,
_SLEFT, S_RIGHT, etc), but it does not do the same thing for the other
modifiers.
@Delapouite
Copy link
Contributor

#2609 (comment)

@Delapouite
Copy link
Contributor

A spreadsheet compiling support for escape sequences in modern terminal:
https://docs.google.com/spreadsheets/d/19W-lXWS9jYwqCK-LwgYo31GucPPxYVld_hVEcfpNpXg/edit#gid=1724051764

@Screwtapello
Copy link
Contributor

If you're going to make a list of terminals and their escape sequences, maybe make it a wiki page?

Apart from the terminals mentioned, rc/base/x11.kak also mentions alacritty and mintty (I assume that's the correct mintty).

It's also worth pointing out that the terminfo database that ships with ncurses is a wealth of information - both for the actual tested descriptions of what sequences are used by which terminals, but also the long and in-depth comments about individual terminals and terminal emulation in general - nearly 50% of the file is comments.

@Delapouite
Copy link
Contributor

I'm not the original author of this document, this spreadsheet was recently shared on lobste.rs by JustinMK, the Neovim leadev.

@Delapouite
Copy link
Contributor

Some abstraction refactoring has started in afd45c6

@mawww
Copy link
Owner

mawww commented Sep 15, 2019

The builtin-terminal-input branch is implementing this, I am keen to merge it but would like a bit of testing on setup different from mine (different terminal emulators mostly).

@alexherbo2
Copy link
Contributor

@mawww I can test it for kitty.

@andreyorst
Copy link
Contributor

seems to work in GNOME Terminal

@mawww
Copy link
Owner

mawww commented Nov 24, 2019

That has been done for a while.

@mawww mawww closed this as completed Nov 24, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants