I'm currently in the process of planning out a custom Vim-like editor. It's going to be written in C and I want it to be as portable as possible between as many types of systems as possible.
I'm aware of curses (ncurses, I suppose), the tput command, and how terminals use control sequence (Esc-[ and the CSI character) to change backgrounds, move the cursor, etc.
Of the options above, it seems like ncurses would be the most recommended way of printing for the editor. BUT ncurses also has a LOT of stuff that I rather wouldn't use, and if it's reasonably feasible I'd rather make my own system. I'm not against using it, but .. anyways.
So, my question is: Is there any way to use control sequences in the vast majority of terminals without using a library? Whether through tput or another method?
Thanks!
tput(1) uses the terminfo(5) (or older termcap(5)) database, which provides the mapping from abstract commands such as move cursor to x,y to escape sequences for different terminals. When you run a command such as
$ tput cup 10 3 # move cursor to row/column 10/3
, the terminfo database is queried to find the correct string for your terminal, which is then simply written to stdout. To find the available commands (e.g., cup), look at the cap-name column in terminfo(5). tput determines what terminal you are using by looking at the TERM environment variable.
(This means that you can check what escape characters are being generated by simply doing $ tput [command] > [file] and opening [file] in some editor that can show control characters, which can be handy for exploration. The infocmp(1) command can also be used for this.)
If you use tput (or the underlying tputs(3)), your program is hence automatically portable to different terminals. This is what Vim uses by the way.
However -- in the modern world, pretty much all terminals (or terminal emulators rather) use ANSI escape codes, along with some extensions (see XTerm Control Sequences). I believe the escapes supported by xterm and their behavior have become something of a de facto standard at this point, with other terminal emulators simply copying xterm's behavior. Some text-based UI libraries like termbox seem to do away with support for non-ANSI terminals altogether, and output ANSI escapes directly.
Besides the already-mentioned termbox, there's also S-Lang, which includes a terminal handling component. I believe those are the two most popular "ncurses replacements". I'd give ncurses some time first though.
Related
I don't understand how to create custom escape sequence for Ctrl+Shift+S like in this question
My problem is that I want to use Ctrl+Shift+S in emacs no-window mode under MacOS terminal. Currently I'm trying to bind this escape sequence to Ctrl+Shift+F12 but I have troubles creating that escape sequence. Maybe there is a batter way to use complex (Ctrl-Shift+anykey) bindings in MacOS terminal with emacs in no-window mode?
You're probably assuming that F12 is a character. It is not. Conventionally, function keys send a sequence of characters.
In Terminal's preferences screen, you can review (most of) the key bindings:
You can also add a key definition (after deciding what characters it ought to send), and associate the Shift and Control modifiers with that key:
For the sake of example, I used the sequence that xterm would send. However, Terminal is not the same as xterm, and you may find other sequences more interesting.
Regarding the related question "What is the correct escape code for control/shift/s": there is no correct escape code because terminals (unless specially configured as illustrated above) do not distinguish between shifted- and unshifted-control characters. To determine what is "correct", you will have to examine your emacs bindings to see what you should send to make it recognize the function you are asking about.
As far as I can see, in Konsole both CTRL+ENTER and ENTER look like byte 13 in the stdin of the running app. But when I run mc which obviously uses ncurses lib, pressing CTRL+ENTER inserts the name of the file into the command line while ENTER opens the file. How is it implemented? I tried to look into sources, but they are totally unreadable for me.
mc (midnight commander) doesn't use ncurses for input, but may use it for output. Essentially it is looking for specific character sequences.
mc makes little use of the terminfo database, essentially only to check on the xterm mouse- and alternate-screen features.
In principle, it could read the user-defined capabilities from the ncurses terminfo database (see for example ncurses trapping extended keys (Control-left, Shift-Function etc)), but it does not.
Since you are looking at sources, see the source of mc, in lib/tty/key.c, which contains tables which mc uses as a set of predefined keys. Doing it that way "works" when mc is configured to use slang, for instance, though it has the drawback that it's hardcoded and may not actually match your terminal.
However - as I said, mc does its own input. Further down in key.c, you may see a chunk in get_modifier() ifdef'd with HAVE_TEXTMODE_X11_SUPPORT. Inside that is a call which ultimately goes to XQueryPointer, which mc uses to find the current state of the modifier keys — if it happens to be running in an X display, and if the feature is enabled. You are probably seeing that.
my ls output colors all directories differently from files, regardless whether I type ls or /bin/ls. I don't have any LS_COLOR stuff set in .bashrc or related files that I can find.
How do I turn off these colors? (I am quite happy with just ls -F)
Thanks!
As noted in comment, OSX ls pays attention to CLICOLOR. The ls manual page is the place to look. It appears to be the same program as in FreeBSD, which uses the terminal database (in contrast to GNU ls). Likewise, note that the variable is LSCOLORS, rather than LS_COLORS:
CLICOLOR
Use ANSI color sequences to distinguish file types. See LSCOLORS below. In addition
to the file types mentioned in the -F option some extra attributes (setuid bit set,
etc.) are also displayed. The colorization is dependent on a terminal type with the
proper termcap(5) capabilities. The default “cons25” console has the proper capabilities,
but to display the colors in an xterm(1), for example, the TERM variable must be
set to “xterm-color”. Other terminal types may require similar adjustments. Colorization
is silently disabled if the output isn't directed to a terminal unless the CLICOLOR_FORCE variable is defined.
CLICOLOR_FORCE
Color sequences are normally disabled if the output isn't directed to a terminal. This
can be overridden by setting this flag. The TERM variable still needs to reference a
color capable terminal however otherwise it is not possible to determine which color
sequences to use.
TERM
The CLICOLOR functionality depends on a terminal type with color capabilities.
The wording about "termcap(5)" is outdated; both FreeBSD and OSX have used terminfo databases for more than ten years.
The GNU ls manual page does say LS_COLORS (the two are not the same). The dircolors manual page makes an oblique reference to a "precompiled database" (this is unrelated to terminfo/termcap, and its use of TERM to get similar results is a source of confusion).
I notice that with the TERM environment variable set to either xterm or xterm-256color that Mac OS X’s Terminal.app utility respects most ANSI escape codes, as least when those escape codes pertain to changing text color.
For example:
echo -e "\033[0;31mERROR:\033[0m It worked"
Produces:
However, I’m more interested in the cursor location manipulation functionality afforded by ANSI escape codes. Unfortunately, that type of code doesn’t seem to work too well in Terminal.app, from what I’ve been able to gather. For example, what I want to do is something like this:
echo -e "\033[sHello world\033[uG'day"
ESC[s Saves the current cursor position, whereas ESC[u restores the last saved position. As a result of running the script above, I’d expect the five characters in “G'day” to overwrite the five characters of “Hello” after the cursor has been repositioned, producing the following:
G'day world
Indeed, this is exactly what I get with iTerm2.app, ConEmu for Windows (running either MinGW or MSYS Git’s copy of bash.exe), etc. What I see in Terminal.app, however, is this:
Hello worldG'day
Is there a reason for this, aside form Terminal.app simply lacking support for these codes? Is there a way to enable this functionality? Is there a chance I have something misconfigured? My TERM setting? Something else?
I’ve been searching everywhere, but haven’t found anything relevant to Terminal.app, specifically. I find it odd that it would support colored text via ANSI escape codes, but not cursor repositioning through the exact same technology. That seems like a fairly arbitrary subset of a fairly well-defined standard. That’s what makes me think I’ve got something misconfigured, rather than that Terminal.app is the one to blame… but, I suppose it’s possible that it simply can’t be done. (Probably one of the reasons iTerm2 exists in the first place?)
If anyone could shed some light on this situation, it’d be much appreciated!
UPDATE
So, I've done a bit more reading and experimenting, and discovered the following oddity:
After looking into n.m.'s answer below, I decided to write out the bytes that were being returned by tput to a file to see how they differed from the regular ANSI instruction.
$ echo "$(tput sc)Hello world$(tput rc)G'day" > out.bin
$ cat -e out.bin
^[7Hello world^[8G'day$
It appears that everything works as expected if I send it the sequences ESC 7 and ESC 8, but NOT if I send it ESC [s and ESC [u, which as I understand things is the more typical representation of the ANSI SCP and RCP codes (Save Cursor Position and Restore Cursor Position, respectively). Since putting ASCII decimal characters 7 or 8 next to an escaped octal byte representation is impossible (\0337 != ESC), environment variables can be used instead to avoid relying on tput:
$ esc=$'\033'
$ csi="${esc}["
$ echo "${csi}0;31mERROR:${csi}0m It worked."
ERROR: It worked. # Color works, as before
$ echo "${csi}sHello world${csi}uG'day"
Hello worldG'day # No dice
$ echo "${esc}7Hello world${esc}8G'day"
G'day world # Success
I'm not sure why this is. If ESC 7 and ESC 8 are some kind of proprietary or custom code for ANSI SCP and RCP that has the potential to vary from terminal implementation to terminal implementation, it would explain to me why tput was created in the first place.
Unfortunately, I am unable to use tput for what I am currently working on, as I am not working exclusively in a bash environment. I am more curious as to how the raw bytes are interpreted from terminal to terminal, and more specifically, whether or not there's a way to get Terminal.app to respect the same ANSI escape codes that all of the other terminal emulators I've tried seem to have no problem with. Is that possible? At this point, I'm beginning to think it simply might not be, which is fine, but it would be nice to know for sure, and possibly also to learn the reason why.
Don't use ANSI codes. Use proper terminfo-based techniques. Xterm-based terminals are not specified to support all ANSI codes. Some do for compatibility, some don't.
Save cursor position sequence is given by the tput sc command and the restore cursor position is tput rc.
echo -e "$(tput sc)Hello world$(tput rc)G'day"
should work on any terminal that supports these sequences.
To see a readable representation of the supported sequences, use the infocmp command. The output might be quite long. If you are interested in sc and rc:
infocmp | grep --color ' [sr]c='
Disclaimer: tested on my Linux machine, don't have a Mac nearby.
UPDATE
Terminal.app is modeled after xterm and xterm is modelled after the VT100 terminal. VT100 did not implement CSI u and CSI s sequences, but used DEC private ESC 7 and ESC 8 sequences (source). Later VT models supported both CSI s/u and ESC 7/8, under different name and with slightly different functionality (source).
ECMA 48 doesn't seem to specify any save/restore cursor position sequences (source (PDF)), or I could not find them there. I don't know where CSU s/u come from. Their name in VT510 documentation suggests they are somehow connected with SCO. This source suggests they are in fact private sequences with no standard meaning. SUN terminals use SCI s to do a reset. It is probably an error to brand these two sequences ANSI.
Modern versions of xterm and other X11 terminal programs (konsole, rxvt...) do support both ESC 7/8 and CSI s/u, but the terminfo database only advertises ESC 7/8. Terminal.app apparently only supports ESC 7/8.
Open up irb and
type gets. It should work fine.
Then try system("choice /c YN") It should work as expected.
Now try gets again, it behaves oddly.
Can someone tell me why this is?
EDIT: For some clarification on the "odd" behavior, it allows me to type for gets, but doesn't show me the characters and I have to press the enter key twice.
Terminal input-output handling is dark and mysterious art. Anyone trying to make colorized output of bash work in windows PowerShell via ssh knows that. (And various shortcutting habits like Ctrl+Backspace only make things worse.)
One of the possible reasons for your problem is special characters handling. Every terminal out there can type characters in number of different modes, and it parses its own output in search for certain character sequences in order to toggle states.
F.e. here one can find ANSI escape code sequences, one of possible supported standards among different kind of terminals.
See there Esc[5;45m? That will make all the following output to blink on magenta background. And there is significantly more stuff like that out there.
So, the answer to your question taken literally is — your choice command messes something with output modes using special escape sequences, and ruby's gets breaks in that quirk special mode of terminal operation.
But more useful will be the link to HighLine gem documentation. Why one might want to implement platform-specific and obtrusive behavior when it is possible to implement the same with about 12 LOC? All the respect for the Gist goes to botimer, I've only stumbled into his code using search.