mirror of
https://github.com/neovim/neovim.git
synced 2024-12-20 03:05:11 -07:00
tui: More refactoring, and improvements to cursor shape support.
The details are in the on-line help under :help cursor-shape . The brief precis is that nvim is following the lead of tmux, and going beyond what tmux does to make cursor shape changes work on a broad range of terminals. This includes on tmux itself, which is no longer bypassed.
This commit is contained in:
parent
03683c375c
commit
76a6509c59
@ -2756,14 +2756,10 @@ A jump table for the options with a short description can be found at |Q_op|.
|
||||
*'guicursor'* *'gcr'* *E545* *E546* *E548* *E549*
|
||||
'guicursor' 'gcr' string (default "n-v-c-sm:block,i-ci-ve:ver25,r-cr-o:hor20")
|
||||
global
|
||||
Configures the cursor style for each mode. Works in the GUI and some
|
||||
terminals.
|
||||
Configures the cursor style for each mode. Works in the GUI and many
|
||||
terminals. See |cursor-shape| for details.
|
||||
|
||||
With tmux you might need this in ~/.tmux.conf (see terminal-overrides
|
||||
in the tmux(1) manual page): >
|
||||
set -ga terminal-overrides ',*:Ss=\E[%p1%d q:Se=\E[2 q'
|
||||
|
||||
< To disable cursor-styling, reset the option: >
|
||||
To disable cursor-styling, reset the option: >
|
||||
:set guicursor=
|
||||
|
||||
< To enable mode shapes, "Cursor" highlight, and blinking: >
|
||||
|
@ -95,10 +95,33 @@ for this extension. So Nvim simply assumes that (all) "dtterm", "xterm",
|
||||
|
||||
*cursor-shape* *termcap-cursor-shape*
|
||||
Nvim will adjust the shape of the cursor from a block to a line when in insert
|
||||
mode, on terminals that support it. If Konsole is detected, it will use the
|
||||
idiosyncratic Konsole terminal control sequences for this, attempting to do
|
||||
the right thing if tmux is between Nvim and Konsole. Otherwise, it uses the
|
||||
conventional DECSCUSR control sequences.
|
||||
mode (or as specified by the 'guicursor' option), on terminals that support
|
||||
it. It uses the same |terminfo| extensions that were pioneered by tmux for
|
||||
this: "Ss" and "Se". If your terminfo definition specifies these, as some
|
||||
(such as "xterm+tmux") do, then nothing more is required.
|
||||
|
||||
If your terminfo definition is missing them, then Nvim will on a wide range of
|
||||
terminals resort to using the conventional DECSUSR control sequence for
|
||||
adjusting the cursor shape. If Konsole is detected, Nvim will use the
|
||||
idiosyncratic Konsole terminal control sequences for this.
|
||||
|
||||
Note: tmux itself accepts the conventional DECSUSR control sequence, the same
|
||||
as many other terminals do. It has to translate this into whatever control
|
||||
sequence is appropriate for the terminal that it is outputting to. Like Nvim,
|
||||
it will use the "Ss" and "Se" capabilities from terminfo if they are present.
|
||||
|
||||
Unlike Nvim, if they are not present in terminfo you will have to add them by
|
||||
setting the tmux "terminal-overrides" setting in $HOME/.tmux.conf . It will
|
||||
appear that Nvim is not changing the cursor, but in fact it is tmux receiving
|
||||
instructions from Nvim to change the cursor and not knowing what to do. See
|
||||
the tmux(1) manual page for the details of how and what to do in the tmux
|
||||
configuration file. It will look something like: >
|
||||
set -ga terminal-overrides '*:Ss=\E[%p1%d q:Se=\E[ q'
|
||||
<or (alas!) for Konsole specifically, something more complex like: >
|
||||
set -ga terminal-overrides \
|
||||
'xterm*:\E]50;CursorShape=%?%p1%{3}%<%t%{0}%e%{1}%;%d\007'
|
||||
<but these are only rough examples that do not include all of the other stuff
|
||||
that occurs in that setting.
|
||||
|
||||
*cs7-problem*
|
||||
Note: If the terminal settings are changed after running Vim, you might have
|
||||
|
@ -44,8 +44,9 @@
|
||||
#define OUTBUF_SIZE 0xffff
|
||||
|
||||
#define TOO_MANY_EVENTS 1000000
|
||||
#define STARTS_WITH(str, prefix) (!memcmp(str, prefix, sizeof(prefix) - 1))
|
||||
#define STARTS_WITH(str, prefix) (!memcmp((str), (prefix), sizeof(prefix) - 1))
|
||||
#define TMUX_WRAP(is_tmux,seq) ((is_tmux) ? "\x1bPtmux;\x1b" seq "\x1b\\" : seq)
|
||||
#define LINUXRESETC "\x1b[?0c"
|
||||
|
||||
typedef struct {
|
||||
int top, bot, left, right;
|
||||
@ -90,7 +91,7 @@ typedef struct {
|
||||
int enable_focus_reporting, disable_focus_reporting;
|
||||
int resize_screen;
|
||||
int reset_scroll_region;
|
||||
int konsole_cursor_shape, dec_cursor_shape;
|
||||
int set_cursor_style, reset_cursor_style;
|
||||
} unibi_ext;
|
||||
} TUIData;
|
||||
|
||||
@ -157,8 +158,8 @@ static void terminfo_start(UI *ui)
|
||||
data->unibi_ext.disable_focus_reporting = -1;
|
||||
data->unibi_ext.resize_screen = -1;
|
||||
data->unibi_ext.reset_scroll_region = -1;
|
||||
data->unibi_ext.konsole_cursor_shape = -1;
|
||||
data->unibi_ext.dec_cursor_shape = -1;
|
||||
data->unibi_ext.set_cursor_style = -1;
|
||||
data->unibi_ext.reset_cursor_style = -1;
|
||||
data->out_fd = 1;
|
||||
data->out_isatty = os_isatty(data->out_fd);
|
||||
// setup unibilium
|
||||
@ -174,7 +175,7 @@ static void terminfo_start(UI *ui)
|
||||
bool iterm = termprg && strstr(termprg, "iTerm.app");
|
||||
bool konsole = os_getenv("KONSOLE_PROFILE_NAME")
|
||||
|| os_getenv("KONSOLE_DBUS_SESSION");
|
||||
patch_terminfo_bugs(data->ut, term, colorterm, vte_version, konsole, iterm);
|
||||
patch_terminfo_bugs(data, term, colorterm, vte_version, konsole, iterm);
|
||||
augment_terminfo(data, term, colorterm, vte_version, konsole, iterm);
|
||||
data->can_change_scroll_region =
|
||||
!!unibi_get_str(data->ut, unibi_change_scroll_region);
|
||||
@ -783,16 +784,6 @@ static void tui_set_mode(UI *ui, ModeShape mode)
|
||||
}
|
||||
}
|
||||
|
||||
switch (shape) {
|
||||
case SHAPE_BLOCK: shape = 0; break;
|
||||
case SHAPE_VER: shape = 1; break;
|
||||
case SHAPE_HOR: shape = 2; break;
|
||||
default: WLOG("Unknown shape value %d", shape); break;
|
||||
}
|
||||
data->params[0].i = shape;
|
||||
data->params[1].i = (c.blinkon != 0);
|
||||
unibi_out(ui, data->unibi_ext.konsole_cursor_shape);
|
||||
|
||||
switch (shape) {
|
||||
case SHAPE_BLOCK: shape = 1; break;
|
||||
case SHAPE_HOR: shape = 3; break;
|
||||
@ -800,7 +791,7 @@ static void tui_set_mode(UI *ui, ModeShape mode)
|
||||
default: WLOG("Unknown shape value %d", shape); break;
|
||||
}
|
||||
data->params[0].i = shape + (int)(c.blinkon == 0);
|
||||
unibi_out(ui, data->unibi_ext.dec_cursor_shape);
|
||||
unibi_out(ui, data->unibi_ext.set_cursor_style);
|
||||
}
|
||||
|
||||
/// @param mode editor mode
|
||||
@ -1142,6 +1133,22 @@ static void unibi_set_if_empty(unibi_term *ut, enum unibi_string str,
|
||||
}
|
||||
}
|
||||
|
||||
static int unibi_find_ext_str(unibi_term *ut, const char *name)
|
||||
{
|
||||
size_t max = unibi_count_ext_bool(ut);
|
||||
for (size_t i = 0; i < max; ++i) {
|
||||
const char * n = unibi_get_ext_str_name(ut, i);
|
||||
if (0 == strcmp(n, name)) {
|
||||
return (int)i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// One creates the dumps from terminfo.src by using
|
||||
// od -t d1 -w
|
||||
// on the compiled files.
|
||||
|
||||
// Taken from unibilium/t/static_xterm.c as of 2015-08-14.
|
||||
// This is an 8-colour terminfo description that lacks
|
||||
// DECSTBN/DECSLRM/DECLRMM capabilities that xterm actually has.
|
||||
@ -1876,25 +1883,39 @@ static unibi_term *load_builtin_terminfo(const char * term)
|
||||
/// terminal types. So patch the terminfo records after loading from an
|
||||
/// external or a built-in database. In an ideal world, the real terminfo data
|
||||
/// would be correct and complete, and this function would be almost empty.
|
||||
static void patch_terminfo_bugs(unibi_term *ut, const char *term,
|
||||
static void patch_terminfo_bugs(TUIData *data, const char *term,
|
||||
const char *colorterm, long vte_version, bool konsole, bool iterm)
|
||||
{
|
||||
unibi_term *ut = data->ut;
|
||||
bool true_xterm = !!os_getenv("XTERM_VERSION");
|
||||
bool xterm = term && STARTS_WITH(term, "xterm");
|
||||
bool mate = colorterm && strstr(colorterm, "mate-terminal");
|
||||
bool gnome = colorterm && strstr(colorterm, "gnome-terminal");
|
||||
bool linux = term && STARTS_WITH(term, "linux");
|
||||
bool rxvt = term && STARTS_WITH(term, "rxvt");
|
||||
bool teraterm = term && STARTS_WITH(term, "teraterm");
|
||||
bool putty = term && STARTS_WITH(term, "putty");
|
||||
bool screen = term && STARTS_WITH(term, "screen");
|
||||
bool tmux_wrap = screen && !!os_getenv("TMUX");
|
||||
|
||||
const char *fix_normal = unibi_get_str(ut, unibi_cursor_normal);
|
||||
char *fix_normal = (char *)unibi_get_str(ut, unibi_cursor_normal);
|
||||
if (fix_normal) {
|
||||
if (STARTS_WITH(fix_normal, "\x1b[?12l")) {
|
||||
// terminfo typically includes DECRST 12 as part of setting up the normal
|
||||
// cursor, which interferes with the user's control via
|
||||
// NVIM_TUI_ENABLE_CURSOR_SHAPE. When DECRST 12 is present, skip over
|
||||
// it, but honor the rest of the TI setting.
|
||||
fix_normal += strlen("\x1b[?12l");
|
||||
// terminfo typically includes DECRST 12 as part of setting up the
|
||||
// normal cursor, which interferes with the user's control via
|
||||
// set_cursor_style. When DECRST 12 is present, skip over it, but honor
|
||||
// the rest of the cnorm setting.
|
||||
fix_normal += sizeof "\x1b[?12l" - 1;
|
||||
unibi_set_str(ut, unibi_cursor_normal, fix_normal);
|
||||
}
|
||||
if (linux
|
||||
&& (strlen(fix_normal) + 1) >= (sizeof LINUXRESETC - 1)
|
||||
&& !memcmp(strchr(fix_normal,0) - (sizeof LINUXRESETC - 1), LINUXRESETC, sizeof LINUXRESETC - 1)) {
|
||||
// The Linux terminfo entry similarly includes a Linux-idiosyncractic
|
||||
// cursor shape reset in cnorm, which similarly interferes with
|
||||
// set_cursor_style.
|
||||
fix_normal[strlen(fix_normal) - (sizeof LINUXRESETC - 1)] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (xterm) {
|
||||
@ -1904,10 +1925,9 @@ static void patch_terminfo_bugs(unibi_term *ut, const char *term,
|
||||
// their own terminal types and terminfo entries, like PuTTY does, and not
|
||||
// claim to be xterm. Or they would mimic xterm properly enough to be
|
||||
// treatable as xterm.
|
||||
#if 0 // We don't need to identify this specifically, for now.
|
||||
#if 0 // We don't need to identify this specifically, for now.
|
||||
bool roxterm = !!os_getenv("ROXTERM_ID");
|
||||
#endif
|
||||
bool true_xterm = !!os_getenv("XTERM_VERSION");
|
||||
unibi_set_if_empty(ut, unibi_to_status_line, "\x1b]0;");
|
||||
unibi_set_if_empty(ut, unibi_from_status_line, "\x07");
|
||||
unibi_set_if_empty(ut, unibi_set_tb_margin, "\x1b[%i%p1%d;%p2%dr");
|
||||
@ -1921,7 +1941,7 @@ static void patch_terminfo_bugs(unibi_term *ut, const char *term,
|
||||
unibi_set_if_empty(ut, unibi_to_status_line, "\x1b]2");
|
||||
unibi_set_if_empty(ut, unibi_from_status_line, "\x07");
|
||||
unibi_set_if_empty(ut, unibi_set_tb_margin, "\x1b[%i%p1%d;%p2%dr");
|
||||
} else if (term && STARTS_WITH(term, "screen")) {
|
||||
} else if (screen) {
|
||||
unibi_set_if_empty(ut, unibi_to_status_line, "\x1b_");
|
||||
unibi_set_if_empty(ut, unibi_from_status_line, "\x1b\\");
|
||||
} else if (term && STARTS_WITH(term, "tmux")) {
|
||||
@ -1968,6 +1988,91 @@ static void patch_terminfo_bugs(unibi_term *ut, const char *term,
|
||||
unibi_set_if_empty(ut, unibi_set_a_background, XTERM_SETAB_16);
|
||||
}
|
||||
}
|
||||
|
||||
// Dickey ncurses terminfo has included the Ss and Se capabilities, pioneered
|
||||
// by tmux, since 2011-07-14. So adding them to terminal types, that do
|
||||
// actually have such control sequences but lack the currect definitions in
|
||||
// terminfo, is a fixup, not an augmentation.
|
||||
data->unibi_ext.reset_cursor_style = unibi_find_ext_str(ut, "Se");
|
||||
data->unibi_ext.set_cursor_style = unibi_find_ext_str(ut, "Ss");
|
||||
if (-1 == data->unibi_ext.set_cursor_style) {
|
||||
// The DECSCUSR sequence to change the cursor shape is widely
|
||||
// supported by several terminal types and should be in many
|
||||
// teminfo entries. See
|
||||
// https://github.com/gnachman/iTerm2/pull/92 for more.
|
||||
// xterm even has an extended version that has a vertical bar.
|
||||
if (true_xterm // per xterm ctlseqs doco (since version 282)
|
||||
// Allows forcing the use of DECSCUSR on linux type terminals, such as
|
||||
// console-terminal-emulator from the nosh toolset, which does indeed
|
||||
// implement the xterm extension:
|
||||
|| (linux && (true_xterm || (vte_version > 0) || colorterm))) {
|
||||
data->unibi_ext.set_cursor_style = (int)unibi_add_ext_str(ut, "Ss",
|
||||
"\x1b[%p1%d q");
|
||||
if (-1 == data->unibi_ext.reset_cursor_style) {
|
||||
data->unibi_ext.reset_cursor_style = (int)unibi_add_ext_str(ut, "Se",
|
||||
"");
|
||||
}
|
||||
unibi_set_ext_str(ut, (size_t)data->unibi_ext.reset_cursor_style, "\x1b[ q");
|
||||
} else if (putty // per MinTTY 0.4.3-1 release notes from 2009
|
||||
|| teraterm // per TeraTerm "Supported Control Functions" doco
|
||||
|| (vte_version >= 3900) // VTE-based terminals since this version.
|
||||
// per tmux manual page and per
|
||||
// https://lists.gnu.org/archive/html/screen-devel/2013-03/msg00000.html
|
||||
|| screen) {
|
||||
// Since we use the xterm extension, we have to map it to the unextended
|
||||
// form.
|
||||
data->unibi_ext.set_cursor_style = (int)unibi_add_ext_str(ut, "Ss",
|
||||
"\x1b[%?"
|
||||
"%p1%{4}%>" "%t%p1%{2}%-" // a bit of a bodge for extension values
|
||||
"%e%p1" // the conventional codes are just passed through
|
||||
"%;%d q");
|
||||
if (-1 == data->unibi_ext.reset_cursor_style) {
|
||||
data->unibi_ext.reset_cursor_style = (int)unibi_add_ext_str(ut, "Se",
|
||||
"");
|
||||
}
|
||||
unibi_set_ext_str(ut, (size_t)data->unibi_ext.reset_cursor_style, "\x1b[ q");
|
||||
} else if (linux) {
|
||||
// Linux uses an idiosyncratic escape code to set the cursor shape and does
|
||||
// not support DECSCUSR.
|
||||
data->unibi_ext.set_cursor_style = (int)unibi_add_ext_str(ut, "Ss",
|
||||
"\x1b[?"
|
||||
"%?"
|
||||
// The parameter passed to Ss is the DECSCUSR parameter, so the
|
||||
// terminal capability has to translate into the Linux idiosyncratic
|
||||
// parameter.
|
||||
"%p1%{2}%<" "%t%{8}" // blink block
|
||||
"%p1%{2}%=" "%t%{24}" // steady block
|
||||
"%p1%{3}%=" "%t%{1}" // blink underline
|
||||
"%p1%{4}%=" "%t%{17}" // steady underline
|
||||
"%p1%{5}%=" "%t%{1}" // blink bar
|
||||
"%p1%{6}%=" "%t%{17}" // steady bar
|
||||
"%e%{0}" // anything else
|
||||
"%;" "%dc");
|
||||
if (-1 == data->unibi_ext.reset_cursor_style) {
|
||||
data->unibi_ext.reset_cursor_style = (int)unibi_add_ext_str(ut, "Se",
|
||||
"");
|
||||
}
|
||||
unibi_set_ext_str(ut, (size_t)data->unibi_ext.reset_cursor_style, "\x1b[?c");
|
||||
} else if (konsole) {
|
||||
// Konsole uses an idiosyncratic escape code to set the cursor shape and does
|
||||
// not support DECSCUSR. The tmux wrapping is unused, now.
|
||||
data->unibi_ext.set_cursor_style = (int)unibi_add_ext_str(ut, "Ss",
|
||||
TMUX_WRAP(tmux_wrap, "\x1b]50;CursorShape=%?"
|
||||
"%p1%{3}%<" "%t%{0}" // block
|
||||
"%e%p1%{4}%<" "%t%{2}" // underline
|
||||
"%e%{1}" // everything else is bar
|
||||
"%;%d;BlinkingCursorEnabled=%?"
|
||||
"%p1%{1}%<" "%t%{1}" // Fortunately if we exclude zero as special,
|
||||
"%e%p1%{1}%&" // in all other cases we can teeat bit #0 as a flag.
|
||||
"%;%d\x07"));
|
||||
if (-1 == data->unibi_ext.reset_cursor_style) {
|
||||
data->unibi_ext.reset_cursor_style = (int)unibi_add_ext_str(ut, "Se",
|
||||
"");
|
||||
}
|
||||
unibi_set_ext_str(ut, (size_t)data->unibi_ext.reset_cursor_style,
|
||||
TMUX_WRAP(tmux_wrap, "\x1b]50;CursorShape=0;BlinkingCursorEnabled=1\x07"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This adds stuff that is not in standard terminfo as extended unibilium
|
||||
@ -1976,15 +2081,16 @@ static void augment_terminfo(TUIData *data, const char *term,
|
||||
const char *colorterm, long vte_version, bool konsole, bool iterm)
|
||||
{
|
||||
unibi_term *ut = data->ut;
|
||||
bool putty = term && STARTS_WITH(term, "putty");
|
||||
bool xterm = term && STARTS_WITH(term, "xterm");
|
||||
bool dtterm = term && STARTS_WITH(term, "dtterm");
|
||||
bool teraterm = term && STARTS_WITH(term, "teraterm");
|
||||
bool rxvt = term && STARTS_WITH(term, "rxvt");
|
||||
bool linux = term && STARTS_WITH(term, "linux");
|
||||
bool tmux_wrap = !!os_getenv("TMUX");
|
||||
bool rxvt = term && STARTS_WITH(term, "rxvt");
|
||||
bool teraterm = term && STARTS_WITH(term, "teraterm");
|
||||
bool putty = term && STARTS_WITH(term, "putty");
|
||||
bool screen = term && STARTS_WITH(term, "screen");
|
||||
bool tmux_wrap = screen && !!os_getenv("TMUX");
|
||||
bool truecolor = colorterm
|
||||
&& (STRCMP(colorterm, "truecolor") || STRCMP(colorterm, "24bit"));
|
||||
&& (0 == strcmp(colorterm, "truecolor") || 0 == strcmp(colorterm, "24bit"));
|
||||
|
||||
// Only define this capability for terminal types that we know understand it.
|
||||
if (dtterm // originated this extension
|
||||
@ -2013,19 +2119,6 @@ static void augment_terminfo(TUIData *data, const char *term,
|
||||
data->unibi_ext.set_cursor_color = (int)unibi_add_ext_str(
|
||||
ut, NULL, "\033]12;#%p1%06x\007");
|
||||
}
|
||||
if (konsole) {
|
||||
// Konsole uses a proprietary escape code to set the cursor shape
|
||||
// and does not support DECSCUSR.
|
||||
data->unibi_ext.konsole_cursor_shape = (int)unibi_add_ext_str(ut, NULL,
|
||||
TMUX_WRAP(tmux_wrap, "\x1b]50;CursorShape=%p1%d;BlinkingCursorEnabled=%p2%d\x07"));
|
||||
} else if (0 == vte_version || vte_version >= 3900) {
|
||||
// Assume that the terminal supports DECSCUSR unless it is an
|
||||
// old VTE based terminal. This should not get wrapped for tmux,
|
||||
// which will handle it via its Ss/Se terminfo extension - usually
|
||||
// according to its terminal-overrides.
|
||||
data->unibi_ext.dec_cursor_shape = (int)unibi_add_ext_str(ut, NULL,
|
||||
"\x1b[%p1%d q");
|
||||
}
|
||||
|
||||
/// Terminals generally ignore private modes that they do not recognize,
|
||||
/// and there is no known ambiguity with these modes from terminal type to
|
||||
|
Loading…
Reference in New Issue
Block a user